summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-11-08 16:22:08 -0300
committerPerberos <[email protected]>2011-11-08 16:22:08 -0300
commitf45852ab2a7126f354079499fc8b03976c0eab27 (patch)
tree885c32d804d3c970c5da41a5527882e8a956b42d /src
downloadmate-system-monitor-f45852ab2a7126f354079499fc8b03976c0eab27.tar.bz2
mate-system-monitor-f45852ab2a7126f354079499fc8b03976c0eab27.tar.xz
initial
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am71
-rw-r--r--src/argv.cpp22
-rw-r--r--src/argv.h18
-rw-r--r--src/bacon-message-connection.c396
-rw-r--r--src/bacon-message-connection.h43
-rw-r--r--src/callbacks.cpp436
-rw-r--r--src/callbacks.h82
-rw-r--r--src/defaulttable.h51
-rw-r--r--src/disks.cpp425
-rw-r--r--src/disks.h12
-rw-r--r--src/e_date.c214
-rw-r--r--src/e_date.h9
-rw-r--r--src/gsm_color_button.c923
-rw-r--r--src/gsm_color_button.h92
-rw-r--r--src/iconthemewrapper.cpp23
-rw-r--r--src/iconthemewrapper.h23
-rw-r--r--src/interface.cpp808
-rw-r--r--src/interface.h32
-rw-r--r--src/load-graph.cpp787
-rw-r--r--src/load-graph.h131
-rw-r--r--src/lsof.cpp408
-rw-r--r--src/lsof.h9
-rw-r--r--src/mate-system-monitor.schemas.in769
-rw-r--r--src/mateconf-keys.cpp14
-rw-r--r--src/mateconf-keys.h19
-rw-r--r--src/memmaps.cpp663
-rw-r--r--src/memmaps.h9
-rw-r--r--src/openfiles.cpp394
-rw-r--r--src/openfiles.h10
-rw-r--r--src/prettytable.cpp256
-rw-r--r--src/prettytable.h62
-rw-r--r--src/procactions.cpp190
-rw-r--r--src/procactions.h28
-rw-r--r--src/procdialogs.cpp842
-rw-r--r--src/procdialogs.h54
-rw-r--r--src/procman.cpp773
-rw-r--r--src/procman.h247
-rw-r--r--src/procman_gksu.cpp54
-rw-r--r--src/procman_gksu.h12
-rw-r--r--src/procman_matesu.cpp40
-rw-r--r--src/procman_matesu.h12
-rw-r--r--src/proctable.cpp964
-rw-r--r--src/proctable.h65
-rw-r--r--src/selection.cpp43
-rw-r--r--src/selection.h21
-rw-r--r--src/selinux.cpp65
-rw-r--r--src/selinux.h14
-rw-r--r--src/smooth_refresh.cpp173
-rw-r--r--src/smooth_refresh.h104
-rw-r--r--src/sysinfo.cpp676
-rw-r--r--src/sysinfo.h9
-rw-r--r--src/util.cpp510
-rw-r--r--src/util.h151
53 files changed, 12258 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..8794e47
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,71 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = \
+ -DPROCMAN_DATADIR=\""$(datadir)/procman/"\" \
+ -DMATELOCALEDIR=\""$(datadir)/locale"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ @PROCMAN_CFLAGS@
+
+bin_PROGRAMS = mate-system-monitor
+
+mate_system_monitor_SOURCES = \
+ argv.h argv.cpp \
+ procman.cpp procman.h \
+ interface.cpp interface.h \
+ callbacks.cpp callbacks.h \
+ load-graph.cpp load-graph.h \
+ proctable.cpp proctable.h \
+ prettytable.cpp prettytable.h \
+ util.cpp util.h \
+ procactions.cpp procactions.h \
+ procdialogs.cpp procdialogs.h \
+ memmaps.cpp memmaps.h \
+ openfiles.cpp openfiles.h \
+ smooth_refresh.cpp smooth_refresh.h \
+ defaulttable.h \
+ disks.cpp disks.h \
+ selinux.h selinux.cpp \
+ procman_matesu.h procman_matesu.cpp \
+ procman_gksu.h procman_gksu.cpp \
+ sysinfo.cpp sysinfo.h \
+ lsof.cpp lsof.h \
+ selection.cpp selection.h \
+ mateconf-keys.cpp mateconf-keys.h \
+ iconthemewrapper.cpp iconthemewrapper.h \
+ e_date.c e_date.h \
+ gsm_color_button.c gsm_color_button.h
+
+
+mate_system_monitor_LDADD = @PROCMAN_LIBS@ libbacon.la
+
+
+noinst_LTLIBRARIES = libbacon.la
+libbacon_la_SOURCES = \
+ bacon-message-connection.c \
+ bacon-message-connection.h
+
+
+specdir = $(datadir)/procman
+
+schemadir = $(MATECONF_SCHEMA_FILE_DIR)
+schema_ins = mate-system-monitor.schemas.in
+schema_DATA = $(schema_ins:.schemas.in=.schemas)
+@INTLTOOL_SCHEMAS_RULE@
+
+EXTRA_DIST = \
+ $(schema_ins)
+
+CLEANFILES = \
+ $(schema_DATA)
+
+if MATECONF_SCHEMAS_INSTALL
+install-data-local:
+ if test -z "$(DESTDIR)"; then \
+ for p in $(schema_DATA); do \
+ MATECONF_CONFIG_SOURCE=$(MATECONF_SCHEMA_CONFIG_SOURCE) \
+ mateconftool-2 --makefile-install-rule $(top_builddir)/src/$$p; \
+ done; \
+ fi
+else
+install-data-local:
+endif
diff --git a/src/argv.cpp b/src/argv.cpp
new file mode 100644
index 0000000..a81a3c0
--- /dev/null
+++ b/src/argv.cpp
@@ -0,0 +1,22 @@
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <glibmm/optionentry.h>
+
+
+#include "argv.h"
+
+namespace procman
+{
+ OptionGroup::OptionGroup()
+ : Glib::OptionGroup("", ""),
+ show_system_tab(false)
+ {
+ Glib::OptionEntry sys_tab;
+ sys_tab.set_long_name("show-system-tab");
+ sys_tab.set_short_name('s');
+ sys_tab.set_description(_("Show the System tab"));
+ this->add_entry(sys_tab, this->show_system_tab);
+ }
+}
+
diff --git a/src/argv.h b/src/argv.h
new file mode 100644
index 0000000..a0f7205
--- /dev/null
+++ b/src/argv.h
@@ -0,0 +1,18 @@
+#ifndef H_PROCMAN_ARGV_1205873424
+#define H_PROCMAN_ARGV_1205873424
+
+#include <glibmm/optiongroup.h>
+
+namespace procman
+{
+ class OptionGroup
+ : public Glib::OptionGroup
+ {
+ public:
+ OptionGroup();
+
+ bool show_system_tab;
+ };
+}
+
+#endif // H_PROCMAN_ARGV_1205873424
diff --git a/src/bacon-message-connection.c b/src/bacon-message-connection.c
new file mode 100644
index 0000000..c8000de
--- /dev/null
+++ b/src/bacon-message-connection.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2003 Bastien Nocera <[email protected]>
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#include "bacon-message-connection.h"
+
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX 108
+#endif
+
+struct BaconMessageConnection {
+ /* A server accepts connections */
+ gboolean is_server;
+
+ /* The socket path itself */
+ char *path;
+
+ /* File descriptor of the socket */
+ int fd;
+ /* Channel to watch */
+ GIOChannel *chan;
+ /* Event id returned by g_io_add_watch() */
+ int conn_id;
+
+ /* Connections accepted by this connection */
+ GSList *accepted_connections;
+
+ /* callback */
+ void (*func) (const char *message, gpointer user_data);
+ gpointer data;
+};
+
+static gboolean
+test_is_socket (const char *path)
+{
+ struct stat s;
+
+ if (stat (path, &s) == -1)
+ return FALSE;
+
+ if (S_ISSOCK (s.st_mode))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+is_owned_by_user_and_socket (const char *path)
+{
+ struct stat s;
+
+ if (stat (path, &s) == -1)
+ return FALSE;
+
+ if (s.st_uid != geteuid ())
+ return FALSE;
+
+ if ((s.st_mode & S_IFSOCK) != S_IFSOCK)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean server_cb (GIOChannel *source,
+ GIOCondition condition, gpointer data);
+
+static gboolean
+setup_connection (BaconMessageConnection *conn)
+{
+ g_return_val_if_fail (conn->chan == NULL, FALSE);
+
+ conn->chan = g_io_channel_unix_new (conn->fd);
+ if (!conn->chan) {
+ return FALSE;
+ }
+ g_io_channel_set_line_term (conn->chan, "\n", 1);
+ conn->conn_id = g_io_add_watch (conn->chan, G_IO_IN, server_cb, conn);
+
+ return TRUE;
+}
+
+static void
+accept_new_connection (BaconMessageConnection *server_conn)
+{
+ BaconMessageConnection *conn;
+ int alen;
+
+ g_return_if_fail (server_conn->is_server);
+
+ conn = g_new0 (BaconMessageConnection, 1);
+ conn->is_server = FALSE;
+ conn->func = server_conn->func;
+ conn->data = server_conn->data;
+
+ conn->fd = accept (server_conn->fd, NULL, (guint *)&alen);
+
+ server_conn->accepted_connections =
+ g_slist_prepend (server_conn->accepted_connections, conn);
+
+ setup_connection (conn);
+}
+
+static gboolean
+server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ BaconMessageConnection *conn = (BaconMessageConnection *)data;
+ char *message, *subs, buf;
+ int cd, rc, offset;
+ gboolean finished;
+
+ offset = 0;
+ if (conn->is_server && conn->fd == g_io_channel_unix_get_fd (source)) {
+ accept_new_connection (conn);
+ return TRUE;
+ }
+ message = g_malloc (1);
+ cd = conn->fd;
+ rc = read (cd, &buf, 1);
+ while (rc > 0 && buf != '\n')
+ {
+ message = g_realloc (message, rc + offset + 1);
+ message[offset] = buf;
+ offset = offset + rc;
+ rc = read (cd, &buf, 1);
+ }
+ if (rc <= 0) {
+ g_io_channel_shutdown (conn->chan, FALSE, NULL);
+ g_io_channel_unref (conn->chan);
+ conn->chan = NULL;
+ close (conn->fd);
+ conn->fd = -1;
+ g_free (message);
+ conn->conn_id = 0;
+
+ return FALSE;
+ }
+ message[offset] = '\0';
+
+ subs = message;
+ finished = FALSE;
+
+ while (finished == FALSE && *subs != '\0')
+ {
+ if (conn->func != NULL)
+ (*conn->func) (subs, conn->data);
+
+ subs += strlen (subs) + 1;
+ if (subs - message >= offset)
+ finished = TRUE;
+ }
+
+ g_free (message);
+
+ return TRUE;
+}
+
+static char *
+find_file_with_pattern (const char *dir, const char *pattern)
+{
+ GDir *filedir;
+ char *found_filename;
+ const char *filename;
+ GPatternSpec *pat;
+
+ filedir = g_dir_open (dir, 0, NULL);
+ if (filedir == NULL)
+ return NULL;
+
+ pat = g_pattern_spec_new (pattern);
+ if (pat == NULL)
+ {
+ g_dir_close (filedir);
+ return NULL;
+ }
+
+ found_filename = NULL;
+
+ while ((filename = g_dir_read_name (filedir)))
+ {
+ if (g_pattern_match_string (pat, filename))
+ {
+ char *tmp = g_build_filename (dir, filename, NULL);
+ if (is_owned_by_user_and_socket (tmp))
+ found_filename = g_strdup (filename);
+ g_free (tmp);
+ }
+
+ if (found_filename != NULL)
+ break;
+ }
+
+ g_pattern_spec_free (pat);
+ g_dir_close (filedir);
+
+ return found_filename;
+}
+
+static char *
+socket_filename (const char *prefix)
+{
+ char *pattern, *newfile, *path, *filename;
+ const char *tmpdir;
+
+ pattern = g_strdup_printf ("%s.%s.*", prefix, g_get_user_name ());
+ tmpdir = g_get_tmp_dir ();
+ filename = find_file_with_pattern (tmpdir, pattern);
+ if (filename == NULL)
+ {
+ newfile = g_strdup_printf ("%s.%s.%u", prefix,
+ g_get_user_name (), g_random_int ());
+ path = g_build_filename (tmpdir, newfile, NULL);
+ g_free (newfile);
+ } else {
+ path = g_build_filename (tmpdir, filename, NULL);
+ g_free (filename);
+ }
+
+ g_free (pattern);
+ return path;
+}
+
+static gboolean
+try_server (BaconMessageConnection *conn)
+{
+ struct sockaddr_un uaddr;
+
+ uaddr.sun_family = AF_UNIX;
+ strncpy (uaddr.sun_path, conn->path,
+ MIN (strlen(conn->path)+1, UNIX_PATH_MAX));
+ conn->fd = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (bind (conn->fd, (struct sockaddr *) &uaddr, sizeof (uaddr)) == -1)
+ {
+ conn->fd = -1;
+ return FALSE;
+ }
+ listen (conn->fd, 5);
+
+ if (!setup_connection (conn))
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean
+try_client (BaconMessageConnection *conn)
+{
+ struct sockaddr_un uaddr;
+
+ uaddr.sun_family = AF_UNIX;
+ strncpy (uaddr.sun_path, conn->path,
+ MIN(strlen(conn->path)+1, UNIX_PATH_MAX));
+ conn->fd = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (connect (conn->fd, (struct sockaddr *) &uaddr,
+ sizeof (uaddr)) == -1)
+ {
+ conn->fd = -1;
+ return FALSE;
+ }
+
+ return setup_connection (conn);
+}
+
+BaconMessageConnection *
+bacon_message_connection_new (const char *prefix)
+{
+ BaconMessageConnection *conn;
+
+ g_return_val_if_fail (prefix != NULL, NULL);
+
+ conn = g_new0 (BaconMessageConnection, 1);
+ conn->path = socket_filename (prefix);
+
+ if (test_is_socket (conn->path) == FALSE)
+ {
+ if (!try_server (conn))
+ {
+ bacon_message_connection_free (conn);
+ return NULL;
+ }
+
+ conn->is_server = TRUE;
+ return conn;
+ }
+
+ if (try_client (conn) == FALSE)
+ {
+ unlink (conn->path);
+ try_server (conn);
+ if (conn->fd == -1)
+ {
+ bacon_message_connection_free (conn);
+ return NULL;
+ }
+
+ conn->is_server = TRUE;
+ return conn;
+ }
+
+ conn->is_server = FALSE;
+ return conn;
+}
+
+void
+bacon_message_connection_free (BaconMessageConnection *conn)
+{
+ GSList *child_conn;
+
+ g_return_if_fail (conn != NULL);
+ /* Only servers can accept other connections */
+ g_return_if_fail (conn->is_server != FALSE ||
+ conn->accepted_connections == NULL);
+
+ child_conn = conn->accepted_connections;
+ while (child_conn != NULL) {
+ bacon_message_connection_free (child_conn->data);
+ child_conn = g_slist_next (child_conn);
+ }
+ g_slist_free (conn->accepted_connections);
+
+ if (conn->conn_id) {
+ g_source_remove (conn->conn_id);
+ conn->conn_id = 0;
+ }
+ if (conn->chan) {
+ g_io_channel_shutdown (conn->chan, FALSE, NULL);
+ g_io_channel_unref (conn->chan);
+ }
+
+ if (conn->is_server != FALSE) {
+ unlink (conn->path);
+ }
+ if (conn->fd != -1) {
+ close (conn->fd);
+ }
+
+ g_free (conn->path);
+ g_free (conn);
+}
+
+void
+bacon_message_connection_set_callback (BaconMessageConnection *conn,
+ BaconMessageReceivedFunc func,
+ gpointer user_data)
+{
+ g_return_if_fail (conn != NULL);
+
+ conn->func = func;
+ conn->data = user_data;
+}
+
+void
+bacon_message_connection_send (BaconMessageConnection *conn,
+ const char *message)
+{
+ g_return_if_fail (conn != NULL);
+ g_return_if_fail (message != NULL);
+
+ g_io_channel_write_chars (conn->chan, message, strlen (message),
+ NULL, NULL);
+ g_io_channel_write_chars (conn->chan, "\n", 1, NULL, NULL);
+ g_io_channel_flush (conn->chan, NULL);
+}
+
+gboolean
+bacon_message_connection_get_is_server (BaconMessageConnection *conn)
+{
+ g_return_val_if_fail (conn != NULL, FALSE);
+
+ return conn->is_server;
+}
+
diff --git a/src/bacon-message-connection.h b/src/bacon-message-connection.h
new file mode 100644
index 0000000..aac7a2d
--- /dev/null
+++ b/src/bacon-message-connection.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003 Bastien Nocera <[email protected]>
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef BACON_MESSAGE_CONNECTION_H
+#define BACON_MESSAGE_CONNECTION_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef void (*BaconMessageReceivedFunc) (const char *message,
+ gpointer user_data);
+
+typedef struct BaconMessageConnection BaconMessageConnection;
+
+BaconMessageConnection *bacon_message_connection_new (const char *prefix);
+void bacon_message_connection_free (BaconMessageConnection *conn);
+void bacon_message_connection_set_callback (BaconMessageConnection *conn,
+ BaconMessageReceivedFunc func,
+ gpointer user_data);
+void bacon_message_connection_send (BaconMessageConnection *conn,
+ const char *message);
+gboolean bacon_message_connection_get_is_server (BaconMessageConnection *conn);
+
+G_END_DECLS
+
+#endif /* BACON_MESSAGE_CONNECTION_H */
diff --git a/src/callbacks.cpp b/src/callbacks.cpp
new file mode 100644
index 0000000..2acaff2
--- /dev/null
+++ b/src/callbacks.cpp
@@ -0,0 +1,436 @@
+/* Procman - callbacks
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <giomm.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <signal.h>
+
+#include "callbacks.h"
+#include "interface.h"
+#include "proctable.h"
+#include "util.h"
+#include "procactions.h"
+#include "procdialogs.h"
+#include "memmaps.h"
+#include "openfiles.h"
+#include "load-graph.h"
+#include "disks.h"
+#include "lsof.h"
+#include "sysinfo.h"
+
+void
+cb_kill_sigstop(GtkAction *action, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ /* no confirmation */
+ kill_process (procdata, SIGSTOP);
+}
+
+
+
+
+void
+cb_kill_sigcont(GtkAction *action, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ /* no confirmation */
+ kill_process (procdata, SIGCONT);
+}
+
+
+
+
+static void
+kill_process_helper(ProcData *procdata, int sig)
+{
+ if (procdata->config.show_kill_warning)
+ procdialog_create_kill_dialog (procdata, sig);
+ else
+ kill_process (procdata, sig);
+}
+
+
+void
+cb_edit_preferences (GtkAction *action, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ procdialog_create_preferences_dialog (procdata);
+}
+
+
+void
+cb_renice (GtkAction *action, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ procdialog_create_renice_dialog (procdata);
+}
+
+
+void
+cb_end_process (GtkAction *action, gpointer data)
+{
+ kill_process_helper(static_cast<ProcData*>(data), SIGTERM);
+}
+
+
+void
+cb_kill_process (GtkAction *action, gpointer data)
+{
+ kill_process_helper(static_cast<ProcData*>(data), SIGKILL);
+}
+
+
+void
+cb_show_memory_maps (GtkAction *action, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ create_memmaps_dialog (procdata);
+}
+
+void
+cb_show_open_files (GtkAction *action, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+
+ create_openfiles_dialog (procdata);
+}
+
+void
+cb_show_lsof(GtkAction *action, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ procman_lsof(procdata);
+}
+
+
+void
+cb_about (GtkAction *action, gpointer data)
+{
+ const gchar * const authors[] = {
+ "Kevin Vandersloot",
+ "Erik Johnsson",
+ "Jorgen Scheibengruber",
+ "Benoît Dejean",
+ "Paolo Borelli",
+ "Karl Lattimer",
+ NULL
+ };
+
+ const gchar * const documenters[] = {
+ "Bill Day",
+ "Sun Microsystems",
+ NULL
+ };
+
+ const gchar * const artists[] = {
+ "Baptiste Mille-Mathias",
+ NULL
+ };
+
+ gtk_show_about_dialog (
+ NULL,
+ "name", _("System Monitor"),
+ "comments", _("View current processes and monitor "
+ "system state"),
+ "version", VERSION,
+ "copyright", "Copyright \xc2\xa9 2001-2004 Kevin Vandersloot\n"
+ "Copyright \xc2\xa9 2005-2007 Benoît Dejean",
+ "logo-icon-name", "utilities-system-monitor",
+ "authors", authors,
+ "artists", artists,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "license", "GPL 2+",
+ "wrap-license", TRUE,
+ NULL
+ );
+}
+
+
+void
+cb_help_contents (GtkAction *action, gpointer data)
+{
+ GError* error = 0;
+ if (!g_app_info_launch_default_for_uri("ghelp:mate-system-monitor", NULL, &error)) {
+ g_warning("Could not display help : %s", error->message);
+ g_error_free(error);
+ }
+}
+
+
+void
+cb_app_exit (GtkAction *action, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ cb_app_delete (NULL, NULL, procdata);
+}
+
+
+gboolean
+cb_app_delete (GtkWidget *window, GdkEventAny *event, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ procman_save_config (procdata);
+ if (procdata->timeout)
+ g_source_remove (procdata->timeout);
+ if (procdata->disk_timeout)
+ g_source_remove (procdata->disk_timeout);
+
+ gtk_main_quit ();
+
+ return TRUE;
+}
+
+
+
+void
+cb_end_process_button_pressed (GtkButton *button, gpointer data)
+{
+ kill_process_helper(static_cast<ProcData*>(data), SIGTERM);
+}
+
+
+static void change_mateconf_color(MateConfClient *client, const char *key,
+ GSMColorButton *cp)
+{
+ GdkColor c;
+ char color[24]; /* color should be 1 + 3*4 + 1 = 15 chars -> 24 */
+
+ gsm_color_button_get_color(cp, &c);
+ g_snprintf(color, sizeof color, "#%04x%04x%04x", c.red, c.green, c.blue);
+ mateconf_client_set_string (client, key, color, NULL);
+}
+
+
+void
+cb_cpu_color_changed (GSMColorButton *cp, gpointer data)
+{
+ char key[80];
+ gint i = GPOINTER_TO_INT (data);
+ MateConfClient *client = mateconf_client_get_default ();
+
+ g_snprintf(key, sizeof key, "/apps/procman/cpu_color%d", i);
+
+ change_mateconf_color(client, key, cp);
+}
+
+void
+cb_mem_color_changed (GSMColorButton *cp, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ change_mateconf_color(procdata->client, "/apps/procman/mem_color", cp);
+}
+
+
+void
+cb_swap_color_changed (GSMColorButton *cp, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ change_mateconf_color(procdata->client, "/apps/procman/swap_color", cp);
+}
+
+void
+cb_net_in_color_changed (GSMColorButton *cp, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ change_mateconf_color(procdata->client, "/apps/procman/net_in_color", cp);
+}
+
+void
+cb_net_out_color_changed (GSMColorButton *cp, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ change_mateconf_color(procdata->client, "/apps/procman/net_out_color", cp);
+}
+
+static void
+get_last_selected (GtkTreeModel *model, GtkTreePath *path,
+ GtkTreeIter *iter, gpointer data)
+{
+ ProcInfo **info = static_cast<ProcInfo**>(data);
+
+ gtk_tree_model_get (model, iter, COL_POINTER, info, -1);
+}
+
+
+void
+cb_row_selected (GtkTreeSelection *selection, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ procdata->selection = selection;
+
+ /* get the most recent selected process and determine if there are
+ ** no selected processes
+ */
+ gtk_tree_selection_selected_foreach (procdata->selection, get_last_selected,
+ &procdata->selected_process);
+
+ update_sensitivity(procdata);
+}
+
+
+gboolean
+cb_tree_button_pressed (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ do_popup_menu (procdata, event);
+
+ return FALSE;
+}
+
+
+gboolean
+cb_tree_popup_menu (GtkWidget *widget, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ do_popup_menu (procdata, NULL);
+
+ return TRUE;
+}
+
+
+void
+cb_switch_page (GtkNotebook *nb, GtkNotebookPage *page,
+ gint num, gpointer data)
+{
+ cb_change_current_page (nb, num, data);
+}
+
+void
+cb_change_current_page (GtkNotebook *nb, gint num, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ procdata->config.current_tab = num;
+
+
+ if (num == PROCMAN_TAB_PROCESSES) {
+
+ cb_timeout (procdata);
+
+ if (!procdata->timeout)
+ procdata->timeout = g_timeout_add (
+ procdata->config.update_interval,
+ cb_timeout, procdata);
+
+ update_sensitivity(procdata);
+ }
+ else {
+ if (procdata->timeout) {
+ g_source_remove (procdata->timeout);
+ procdata->timeout = 0;
+ }
+
+ update_sensitivity(procdata);
+ }
+
+
+ if (num == PROCMAN_TAB_RESOURCES) {
+ load_graph_start (procdata->cpu_graph);
+ load_graph_start (procdata->mem_graph);
+ load_graph_start (procdata->net_graph);
+ }
+ else {
+ load_graph_stop (procdata->cpu_graph);
+ load_graph_stop (procdata->mem_graph);
+ load_graph_stop (procdata->net_graph);
+ }
+
+
+ if (num == PROCMAN_TAB_DISKS) {
+
+ cb_update_disks (procdata);
+
+ if(!procdata->disk_timeout) {
+ procdata->disk_timeout =
+ g_timeout_add (procdata->config.disks_update_interval,
+ cb_update_disks,
+ procdata);
+ }
+ }
+ else {
+ if(procdata->disk_timeout) {
+ g_source_remove (procdata->disk_timeout);
+ procdata->disk_timeout = 0;
+ }
+ }
+
+ if (num == PROCMAN_TAB_SYSINFO) {
+ procman::build_sysinfo_ui();
+ }
+}
+
+
+
+gint
+cb_user_refresh (GtkAction*, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ proctable_update_all(procdata);
+ return FALSE;
+}
+
+
+gint
+cb_timeout (gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ guint new_interval;
+
+ proctable_update_all (procdata);
+
+ if (procdata->smooth_refresh->get(new_interval))
+ {
+ procdata->timeout = g_timeout_add(new_interval,
+ cb_timeout,
+ procdata);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void
+cb_radio_processes(GtkAction *action, GtkRadioAction *current, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+
+ procdata->config.whose_process = gtk_radio_action_get_current_value(current);
+
+ mateconf_client_set_int (procdata->client, "/apps/procman/view_as",
+ procdata->config.whose_process, NULL);
+}
diff --git a/src/callbacks.h b/src/callbacks.h
new file mode 100644
index 0000000..4c84793
--- /dev/null
+++ b/src/callbacks.h
@@ -0,0 +1,82 @@
+/* Procman - callbacks
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _PROCMAN_CALLBACKS_H_
+#define _PROCMAN_CALLBACKS_H_
+
+#include <gtk/gtk.h>
+#include "procman.h"
+#include "gsm_color_button.h"
+
+
+void cb_show_memory_maps (GtkAction *action, gpointer data);
+void cb_show_open_files (GtkAction *action, gpointer data);
+void cb_show_lsof(GtkAction *action, gpointer data);
+void cb_renice (GtkAction *action, gpointer data);
+void cb_end_process (GtkAction *action, gpointer data);
+void cb_kill_process (GtkAction *action, gpointer data);
+void cb_edit_preferences (GtkAction *action, gpointer data);
+
+void cb_help_contents (GtkAction *action, gpointer data);
+void cb_about (GtkAction *action, gpointer data);
+
+void cb_app_exit (GtkAction *action, gpointer data);
+gboolean cb_app_delete (GtkWidget *window, GdkEventAny *event, gpointer data);
+
+void cb_end_process_button_pressed (GtkButton *button, gpointer data);
+void cb_logout (GtkButton *button, gpointer data);
+
+void cb_info_button_pressed (GtkButton *button, gpointer user_data);
+
+void cb_cpu_color_changed (GSMColorButton *widget, gpointer user_data);
+void cb_mem_color_changed (GSMColorButton *widget, gpointer user_data);
+void cb_swap_color_changed (GSMColorButton *widget, gpointer user_data);
+void cb_net_in_color_changed (GSMColorButton *widget, gpointer user_data);
+void cb_net_out_color_changed (GSMColorButton *widget, gpointer user_data);
+
+void cb_row_selected (GtkTreeSelection *selection, gpointer data);
+
+gboolean cb_tree_popup_menu (GtkWidget *widget, gpointer data);
+gboolean cb_tree_button_pressed (GtkWidget *widget, GdkEventButton *event,
+ gpointer data);
+
+
+void cb_change_current_page (GtkNotebook *nb,
+ gint num, gpointer data);
+void cb_switch_page (GtkNotebook *nb, GtkNotebookPage *page,
+ gint num, gpointer data);
+
+gint cb_update_disks (gpointer data);
+gint cb_user_refresh (GtkAction* action, gpointer data);
+gint cb_timeout (gpointer data);
+
+void cb_radio_processes(GtkAction *action,
+ GtkRadioAction *current,
+ gpointer data);
+
+
+
+void cb_kill_sigstop(GtkAction *action,
+ gpointer data);
+
+void cb_kill_sigcont(GtkAction *action,
+ gpointer data);
+
+#endif /* _PROCMAN_CALLBACKS_H_ */
diff --git a/src/defaulttable.h b/src/defaulttable.h
new file mode 100644
index 0000000..6b5a096
--- /dev/null
+++ b/src/defaulttable.h
@@ -0,0 +1,51 @@
+#ifndef _PROCMAN_DEFAULTTABLE_H_
+#define _PROCMAN_DEFAULTTABLE_H_
+
+#include <string>
+#include <glibmm/refptr.h>
+#include <glibmm/regex.h>
+
+/* This file contains prettynames and icons for well-known applications, that by default has no .desktop entry */
+
+struct PrettyTableItem
+{
+ Glib::RefPtr<Glib::Regex> command;
+ std::string icon;
+
+ PrettyTableItem(const std::string& a_command, const std::string& a_icon)
+ : command(Glib::Regex::create("^(" + a_command + ")$")),
+ icon(a_icon)
+ { }
+};
+
+#define ITEM PrettyTableItem
+
+/* The current table is only a test */
+static const PrettyTableItem default_table[] = {
+ ITEM("(ba|z|tc|c|k)?sh", "utilities-terminal"),
+ ITEM("(k|sys|u)logd|logger", "internet-news-reader"),
+ ITEM("X(org)?", "display"),
+ ITEM("apache2?|httpd|lighttpd", "internet-web-browser"),
+ ITEM(".*applet(-?2)?", "mate-applets"),
+ ITEM("atd|cron|CRON|ntpd", "date"),
+ ITEM("cupsd|lpd?", "printer"),
+ ITEM("cvsd|mtn|git|svn", "file-manager"),
+ ITEM("emacs(server|\\d+)?", "mate-emacs"),
+ ITEM("evolution.*", "internet-mail"),
+ ITEM("famd|gam_server", "file-manager"),
+ ITEM("mateconfd-2", "preferences-desktop"),
+ ITEM("getty", "input-keyboard"),
+ ITEM("gdb|((gcc|g\\+\\+)(-.*)?)|ar|ld|make", "applications-development"),
+ ITEM("marco", "mate-window-manager"),
+ ITEM("sendmail|exim\\d?", "internet-mail"),
+ ITEM("squid", "proxy"),
+ ITEM("ssh(d|-agent)", "ssh-askpass-mate"),
+ ITEM("top|vmstat", "system-monitor"),
+ ITEM("vim?", "vim"),
+ ITEM("x?inetd", "internet-web-browser"),
+ ITEM("vino.*", "mate-remote-desktop")
+};
+
+#undef ITEM
+
+#endif /* _PROCMAN_DEFAULTTABLE_H_ */
diff --git a/src/disks.cpp b/src/disks.cpp
new file mode 100644
index 0000000..172fc7a
--- /dev/null
+++ b/src/disks.cpp
@@ -0,0 +1,425 @@
+#include <config.h>
+
+#include <giomm.h>
+#include <giomm/themedicon.h>
+#include <gtk/gtk.h>
+#include <glibtop/mountlist.h>
+#include <glibtop/fsusage.h>
+#include <glib/gi18n.h>
+
+#include "procman.h"
+#include "disks.h"
+#include "util.h"
+#include "interface.h"
+#include "iconthemewrapper.h"
+
+enum DiskColumns
+{
+ /* string columns* */
+ DISK_DEVICE,
+ DISK_DIR,
+ DISK_TYPE,
+ DISK_TOTAL,
+ DISK_FREE,
+ DISK_AVAIL,
+ /* USED has to be the last column */
+ DISK_USED,
+ // then unvisible columns
+ /* PixBuf column */
+ DISK_ICON,
+ /* numeric columns */
+ DISK_USED_PERCENTAGE,
+ DISK_N_COLUMNS
+};
+
+
+
+static void
+fsusage_stats(const glibtop_fsusage *buf,
+ guint64 *bused, guint64 *bfree, guint64 *bavail, guint64 *btotal,
+ gint *percentage)
+{
+ guint64 total = buf->blocks * buf->block_size;
+
+ if (!total) {
+ /* not a real device */
+ *btotal = *bfree = *bavail = *bused = 0ULL;
+ *percentage = 0;
+ } else {
+ int percent;
+ *btotal = total;
+ *bfree = buf->bfree * buf->block_size;
+ *bavail = buf->bavail * buf->block_size;
+ *bused = *btotal - *bfree;
+ /* percent = 100.0f * *bused / *btotal; */
+ percent = 100 * *bused / (*bused + *bavail);
+ *percentage = CLAMP(percent, 0, 100);
+ }
+}
+
+
+namespace
+{
+ string get_icon_for_path(const std::string& path)
+ {
+ using namespace Glib;
+ using namespace Gio;
+
+ // FIXME: I don't know whether i should use Volume or Mount or UnixMount
+ // all i need an icon name.
+ RefPtr<VolumeMonitor> monitor = VolumeMonitor::get();
+
+ std::vector<RefPtr<Mount> > mounts = monitor->get_mounts();
+
+ for (size_t i = 0; i != mounts.size(); ++i) {
+ if (mounts[i]->get_name() != path)
+ continue;
+
+ RefPtr<Icon> icon = mounts[i]->get_icon();
+ RefPtr<ThemedIcon> themed_icon = RefPtr<ThemedIcon>::cast_dynamic(icon);
+
+ if (themed_icon) {
+ char* name = 0;
+ // FIXME: not wrapped yet
+ g_object_get(G_OBJECT(themed_icon->gobj()), "name", &name, NULL);
+ return make_string(name);
+ }
+ }
+
+ return "";
+ }
+}
+
+
+static Glib::RefPtr<Gdk::Pixbuf>
+get_icon_for_device(const char *mountpoint)
+{
+ procman::IconThemeWrapper icon_theme;
+ string icon_name = get_icon_for_path(mountpoint);
+ if (icon_name == "")
+ // FIXME: defaults to a safe value
+ icon_name = "drive-harddisk"; // get_icon_for_path("/");
+ return icon_theme->load_icon(icon_name, 24, Gtk::ICON_LOOKUP_USE_BUILTIN);
+}
+
+
+static gboolean
+find_disk_in_model(GtkTreeModel *model, const char *mountpoint,
+ GtkTreeIter *result)
+{
+ GtkTreeIter iter;
+ gboolean found = FALSE;
+
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ char *dir;
+
+ gtk_tree_model_get(model, &iter,
+ DISK_DIR, &dir,
+ -1);
+
+ if (dir && !strcmp(dir, mountpoint)) {
+ *result = iter;
+ found = TRUE;
+ }
+
+ g_free(dir);
+
+ } while (!found && gtk_tree_model_iter_next(model, &iter));
+ }
+
+ return found;
+}
+
+
+
+static void
+remove_old_disks(GtkTreeModel *model, const glibtop_mountentry *entries, guint n)
+{
+ GtkTreeIter iter;
+
+ if (!gtk_tree_model_get_iter_first(model, &iter))
+ return;
+
+ while (true) {
+ char *dir;
+ guint i;
+ gboolean found = FALSE;
+
+ gtk_tree_model_get(model, &iter,
+ DISK_DIR, &dir,
+ -1);
+
+ for (i = 0; i != n; ++i) {
+ if (!strcmp(dir, entries[i].mountdir)) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ g_free(dir);
+
+ if (!found) {
+ if (!gtk_list_store_remove(GTK_LIST_STORE(model), &iter))
+ break;
+ else
+ continue;
+ }
+
+ if (!gtk_tree_model_iter_next(model, &iter))
+ break;
+ }
+}
+
+
+
+static void
+add_disk(GtkListStore *list, const glibtop_mountentry *entry, bool show_all_fs)
+{
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf;
+ GtkTreeIter iter;
+ glibtop_fsusage usage;
+ guint64 bused, bfree, bavail, btotal;
+ gint percentage;
+
+ glibtop_get_fsusage(&usage, entry->mountdir);
+
+ if (not show_all_fs and usage.blocks == 0) {
+ if (find_disk_in_model(GTK_TREE_MODEL(list), entry->mountdir, &iter))
+ gtk_list_store_remove(list, &iter);
+ return;
+ }
+
+ fsusage_stats(&usage, &bused, &bfree, &bavail, &btotal, &percentage);
+ pixbuf = get_icon_for_device(entry->mountdir);
+
+ /* if we can find a row with the same mountpoint, we get it but we
+ still need to update all the fields.
+ This makes selection persistent.
+ */
+ if (!find_disk_in_model(GTK_TREE_MODEL(list), entry->mountdir, &iter))
+ gtk_list_store_append(list, &iter);
+
+ gtk_list_store_set(list, &iter,
+ DISK_ICON, pixbuf->gobj(),
+ DISK_DEVICE, entry->devname,
+ DISK_DIR, entry->mountdir,
+ DISK_TYPE, entry->type,
+ DISK_USED_PERCENTAGE, percentage,
+ DISK_TOTAL, btotal,
+ DISK_FREE, bfree,
+ DISK_AVAIL, bavail,
+ DISK_USED, bused,
+ -1);
+}
+
+
+
+int
+cb_update_disks(gpointer data)
+{
+ ProcData *const procdata = static_cast<ProcData*>(data);
+
+ GtkListStore *list;
+ glibtop_mountentry * entries;
+ glibtop_mountlist mountlist;
+ guint i;
+
+ list = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(procdata->disk_list)));
+
+ entries = glibtop_get_mountlist(&mountlist, procdata->config.show_all_fs);
+
+ remove_old_disks(GTK_TREE_MODEL(list), entries, mountlist.number);
+
+ for (i = 0; i < mountlist.number; i++)
+ add_disk(list, &entries[i], procdata->config.show_all_fs);
+
+ g_free(entries);
+
+ return TRUE;
+}
+
+
+static void
+cb_disk_columns_changed(GtkTreeView *treeview, gpointer user_data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(user_data);
+
+ procman_save_tree_state(procdata->client,
+ GTK_WIDGET(treeview),
+ "/apps/procman/disktreenew");
+}
+
+
+static void open_dir(GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ char *dir, *url;
+
+ model = gtk_tree_view_get_model(tree_view);
+
+ if (!gtk_tree_model_get_iter(model, &iter, path)) {
+ char *p;
+ p = gtk_tree_path_to_string(path);
+ g_warning("Cannot get iter for path '%s'\n", p);
+ g_free(p);
+ return;
+ }
+
+ gtk_tree_model_get(model, &iter, DISK_DIR, &dir, -1);
+
+ url = g_strdup_printf("file://%s", dir);
+
+ GError* error = 0;
+ if (!g_app_info_launch_default_for_uri(url, NULL, &error)) {
+ g_warning("Cannot open '%s' : %s\n", url, error->message);
+ g_error_free(error);
+ }
+
+ g_free(url);
+ g_free(dir);
+}
+
+GtkWidget *
+create_disk_view(ProcData *procdata)
+{
+ GtkWidget *disk_box;
+ GtkWidget *label;
+ GtkWidget *scrolled;
+ GtkWidget *disk_tree;
+ GtkListStore *model;
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *cell;
+ guint i;
+
+ const gchar * const titles[] = {
+ N_("Device"),
+ N_("Directory"),
+ N_("Type"),
+ N_("Total"),
+ N_("Free"),
+ N_("Available"),
+ N_("Used")
+ };
+
+ disk_box = gtk_vbox_new(FALSE, 6);
+
+ gtk_container_set_border_width(GTK_CONTAINER(disk_box), 12);
+
+ label = make_title_label(_("File Systems"));
+ gtk_box_pack_start(GTK_BOX(disk_box), label, FALSE, FALSE, 0);
+
+ scrolled = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
+ GTK_SHADOW_IN);
+
+ gtk_box_pack_start(GTK_BOX(disk_box), scrolled, TRUE, TRUE, 0);
+
+ model = gtk_list_store_new(DISK_N_COLUMNS, /* n columns */
+ G_TYPE_STRING, /* DISK_DEVICE */
+ G_TYPE_STRING, /* DISK_DIR */
+ G_TYPE_STRING, /* DISK_TYPE */
+ G_TYPE_UINT64, /* DISK_TOTAL */
+ G_TYPE_UINT64, /* DISK_FREE */
+ G_TYPE_UINT64, /* DISK_AVAIL */
+ G_TYPE_UINT64, /* DISK_USED */
+ GDK_TYPE_PIXBUF, /* DISK_ICON */
+ G_TYPE_INT /* DISK_USED_PERCENTAGE */
+ );
+
+ disk_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
+ g_signal_connect(G_OBJECT(disk_tree), "row-activated", G_CALLBACK(open_dir), NULL);
+ procdata->disk_list = disk_tree;
+ gtk_container_add(GTK_CONTAINER(scrolled), disk_tree);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(disk_tree), TRUE);
+ g_object_unref(G_OBJECT(model));
+
+ /* icon + device */
+
+ col = gtk_tree_view_column_new();
+ cell = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(col, cell, FALSE);
+ gtk_tree_view_column_set_attributes(col, cell, "pixbuf", DISK_ICON,
+ NULL);
+
+ cell = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(col, cell, FALSE);
+ gtk_tree_view_column_set_attributes(col, cell, "text", DISK_DEVICE,
+ NULL);
+ gtk_tree_view_column_set_title(col, _(titles[DISK_DEVICE]));
+ gtk_tree_view_column_set_sort_column_id(col, DISK_DEVICE);
+ gtk_tree_view_column_set_reorderable(col, TRUE);
+ gtk_tree_view_column_set_resizable(col, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(disk_tree), col);
+
+ /* sizes - used */
+
+ for (i = DISK_DIR; i <= DISK_AVAIL; i++) {
+ cell = gtk_cell_renderer_text_new();
+ col = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(col, cell, TRUE);
+ gtk_tree_view_column_set_title(col, _(titles[i]));
+ gtk_tree_view_column_set_resizable(col, TRUE);
+ gtk_tree_view_column_set_sort_column_id(col, i);
+ gtk_tree_view_column_set_reorderable(col, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(disk_tree), col);
+
+ switch (i) {
+ case DISK_TOTAL:
+ case DISK_FREE:
+ case DISK_AVAIL:
+ g_object_set(cell, "xalign", 1.0f, NULL);
+ gtk_tree_view_column_set_cell_data_func(col, cell,
+ &procman::size_cell_data_func,
+ GUINT_TO_POINTER(i),
+ NULL);
+ break;
+
+ default:
+ gtk_tree_view_column_set_attributes(col, cell,
+ "text", i,
+ NULL);
+ break;
+ }
+ }
+
+ /* used + percentage */
+
+ col = gtk_tree_view_column_new();
+ cell = gtk_cell_renderer_text_new();
+ g_object_set(cell, "xalign", 1.0f, NULL);
+ gtk_tree_view_column_pack_start(col, cell, FALSE);
+ gtk_tree_view_column_set_cell_data_func(col, cell,
+ &procman::size_cell_data_func,
+ GUINT_TO_POINTER(DISK_USED),
+ NULL);
+ gtk_tree_view_column_set_title(col, _(titles[DISK_USED]));
+
+ cell = gtk_cell_renderer_progress_new();
+ gtk_tree_view_column_pack_start(col, cell, TRUE);
+ gtk_tree_view_column_set_attributes(col, cell, "value",
+ DISK_USED_PERCENTAGE, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(disk_tree), col);
+ gtk_tree_view_column_set_resizable(col, TRUE);
+ gtk_tree_view_column_set_sort_column_id(col, DISK_USED);
+ gtk_tree_view_column_set_reorderable(col, TRUE);
+
+ /* numeric sort */
+
+ gtk_widget_show_all(disk_box);
+
+ procman_get_tree_state(procdata->client, disk_tree,
+ "/apps/procman/disktreenew");
+
+ g_signal_connect (G_OBJECT(disk_tree), "columns-changed",
+ G_CALLBACK(cb_disk_columns_changed), procdata);
+
+ return disk_box;
+}
diff --git a/src/disks.h b/src/disks.h
new file mode 100644
index 0000000..c62c47a
--- /dev/null
+++ b/src/disks.h
@@ -0,0 +1,12 @@
+#ifndef H_MATE_SYSTEM_MONITOR_DISKS_1123719137
+#define H_MATE_SYSTEM_MONITOR_DISKS_1123719137
+
+#include "procman.h"
+
+GtkWidget *
+create_disk_view(ProcData *procdata);
+
+int
+cb_update_disks(gpointer procdata);
+
+#endif /* H_MATE_SYSTEM_MONITOR_DISKLIST_1123719137 */
diff --git a/src/e_date.c b/src/e_date.c
new file mode 100644
index 0000000..9b9a894
--- /dev/null
+++ b/src/e_date.c
@@ -0,0 +1,214 @@
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+#include "e_date.h"
+
+/*
+ all this code comes from evolution
+ - e-util.c
+ - message-list.c
+*/
+
+
+static size_t e_strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
+{
+#ifdef HAVE_LKSTRFTIME
+ return strftime(s, max, fmt, tm);
+#else
+ char *c, *ffmt, *ff;
+ size_t ret;
+
+ ffmt = g_strdup(fmt);
+ ff = ffmt;
+ while ((c = strstr(ff, "%l")) != NULL) {
+ c[1] = 'I';
+ ff = c;
+ }
+
+ ff = ffmt;
+ while ((c = strstr(ff, "%k")) != NULL) {
+ c[1] = 'H';
+ ff = c;
+ }
+
+ ret = strftime(s, max, ffmt, tm);
+ g_free(ffmt);
+ return ret;
+#endif
+}
+
+
+/**
+ * Function to do a last minute fixup of the AM/PM stuff if the locale
+ * and gettext haven't done it right. Most English speaking countries
+ * except the USA use the 24 hour clock (UK, Australia etc). However
+ * since they are English nobody bothers to write a language
+ * translation (gettext) file. So the locale turns off the AM/PM, but
+ * gettext does not turn on the 24 hour clock. Leaving a mess.
+ *
+ * This routine checks if AM/PM are defined in the locale, if not it
+ * forces the use of the 24 hour clock.
+ *
+ * The function itself is a front end on strftime and takes exactly
+ * the same arguments.
+ *
+ * TODO: Actually remove the '%p' from the fixed up string so that
+ * there isn't a stray space.
+ **/
+
+static size_t e_strftime_fix_am_pm(char *s, size_t max, const char *fmt, const struct tm *tm)
+{
+ char buf[10];
+ char *sp;
+ char *ffmt;
+ size_t ret;
+
+ if (strstr(fmt, "%p")==NULL && strstr(fmt, "%P")==NULL) {
+ /* No AM/PM involved - can use the fmt string directly */
+ ret=e_strftime(s, max, fmt, tm);
+ } else {
+ /* Get the AM/PM symbol from the locale */
+ e_strftime (buf, 10, "%p", tm);
+
+ if (buf[0]) {
+ /**
+ * AM/PM have been defined in the locale
+ * so we can use the fmt string directly
+ **/
+ ret=e_strftime(s, max, fmt, tm);
+ } else {
+ /**
+ * No AM/PM defined by locale
+ * must change to 24 hour clock
+ **/
+ ffmt=g_strdup(fmt);
+ for (sp=ffmt; (sp=strstr(sp, "%l")); sp++) {
+ /**
+ * Maybe this should be 'k', but I have never
+ * seen a 24 clock actually use that format
+ **/
+ sp[1]='H';
+ }
+ for (sp=ffmt; (sp=strstr(sp, "%I")); sp++) {
+ sp[1]='H';
+ }
+ ret=e_strftime(s, max, ffmt, tm);
+ g_free(ffmt);
+ }
+ }
+ return(ret);
+}
+
+static size_t
+e_utf8_strftime_fix_am_pm(char *s, size_t max, const char *fmt, const struct tm *tm)
+{
+ size_t sz, ret;
+ char *locale_fmt, *buf;
+
+ locale_fmt = g_locale_from_utf8(fmt, -1, NULL, &sz, NULL);
+ if (!locale_fmt)
+ return 0;
+
+ ret = e_strftime_fix_am_pm(s, max, locale_fmt, tm);
+ if (!ret) {
+ g_free (locale_fmt);
+ return 0;
+ }
+
+ buf = g_locale_to_utf8(s, ret, NULL, &sz, NULL);
+ if (!buf) {
+ g_free (locale_fmt);
+ return 0;
+ }
+
+ if (sz >= max) {
+ char *tmp = buf + max - 1;
+ tmp = g_utf8_find_prev_char(buf, tmp);
+ if (tmp)
+ sz = tmp - buf;
+ else
+ sz = 0;
+ }
+ memcpy(s, buf, sz);
+ s[sz] = '\0';
+ g_free(locale_fmt);
+ g_free(buf);
+ return sz;
+}
+
+
+static char *
+filter_date (time_t date)
+{
+ time_t nowdate = time(NULL);
+ time_t yesdate;
+ struct tm then, now, yesterday;
+ char buf[26];
+ gboolean done = FALSE;
+
+ if (date == 0)
+ // xgettext: ? stands for unknown
+ return g_strdup (_("?"));
+
+ localtime_r (&date, &then);
+ localtime_r (&nowdate, &now);
+ if (then.tm_mday == now.tm_mday &&
+ then.tm_mon == now.tm_mon &&
+ then.tm_year == now.tm_year) {
+ e_utf8_strftime_fix_am_pm (buf, 26, _("Today %l:%M %p"), &then);
+ done = TRUE;
+ }
+ if (!done) {
+ yesdate = nowdate - 60 * 60 * 24;
+ localtime_r (&yesdate, &yesterday);
+ if (then.tm_mday == yesterday.tm_mday &&
+ then.tm_mon == yesterday.tm_mon &&
+ then.tm_year == yesterday.tm_year) {
+ e_utf8_strftime_fix_am_pm (buf, 26, _("Yesterday %l:%M %p"), &then);
+ done = TRUE;
+ }
+ }
+ if (!done) {
+ int i;
+ for (i = 2; i < 7; i++) {
+ yesdate = nowdate - 60 * 60 * 24 * i;
+ localtime_r (&yesdate, &yesterday);
+ if (then.tm_mday == yesterday.tm_mday &&
+ then.tm_mon == yesterday.tm_mon &&
+ then.tm_year == yesterday.tm_year) {
+ e_utf8_strftime_fix_am_pm (buf, 26, _("%a %l:%M %p"), &then);
+ done = TRUE;
+ break;
+ }
+ }
+ }
+ if (!done) {
+ if (then.tm_year == now.tm_year) {
+ e_utf8_strftime_fix_am_pm (buf, 26, _("%b %d %l:%M %p"), &then);
+ } else {
+ e_utf8_strftime_fix_am_pm (buf, 26, _("%b %d %Y"), &then);
+ }
+ }
+#if 0
+#ifdef CTIME_R_THREE_ARGS
+ ctime_r (&date, buf, 26);
+#else
+ ctime_r (&date, buf);
+#endif
+#endif
+
+ return g_strdup (buf);
+}
+
+
+
+
+char *
+procman_format_date_for_display(time_t d)
+{
+ return filter_date(d);
+}
diff --git a/src/e_date.h b/src/e_date.h
new file mode 100644
index 0000000..28dfd13
--- /dev/null
+++ b/src/e_date.h
@@ -0,0 +1,9 @@
+#ifndef H_PROCMAN_E_DATE_1135695432
+#define H_PROCMAN_E_DATE_1135695432
+
+#include <time.h>
+
+char *
+procman_format_date_for_display(time_t d);
+
+#endif /* H_PROCMAN_E_DATE_1135695432 */
diff --git a/src/gsm_color_button.c b/src/gsm_color_button.c
new file mode 100644
index 0000000..ce72cc1
--- /dev/null
+++ b/src/gsm_color_button.c
@@ -0,0 +1,923 @@
+/*
+ * Mate system monitor colour pickers
+ * Copyright (C) 2007 Karl Lattimer <[email protected]>
+ * All rights reserved.
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with the software; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.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 <cairo.h>
+
+#include "gsm_color_button.h"
+
+#define GSM_COLOR_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GSM_TYPE_COLOR_BUTTON, GSMColorButtonPrivate))
+
+struct _GSMColorButtonPrivate
+{
+ GtkWidget *cs_dialog; /* Color selection dialog */
+
+ gchar *title; /* Title for the color selection window */
+
+ GdkColor color;
+
+ gdouble fraction; /* Only used by GSMCP_TYPE_PIE */
+ guint type;
+ cairo_surface_t *image_buffer;
+ gdouble highlight;
+};
+
+/* Properties */
+enum
+{
+ PROP_0,
+ PROP_PERCENTAGE,
+ PROP_TITLE,
+ PROP_COLOR,
+ PROP_TYPE
+};
+
+/* Signals */
+enum
+{
+ COLOR_SET,
+ LAST_SIGNAL
+};
+
+#define GSMCP_MIN_WIDTH 15
+#define GSMCP_MIN_HEIGHT 15
+
+static void gsm_color_button_class_intern_init (gpointer);
+static void gsm_color_button_class_init (GSMColorButtonClass * klass);
+static void gsm_color_button_init (GSMColorButton * color_button);
+static void gsm_color_button_finalize (GObject * object);
+static void gsm_color_button_set_property (GObject * object, guint param_id,
+ const GValue * value,
+ GParamSpec * pspec);
+static void gsm_color_button_get_property (GObject * object, guint param_id,
+ GValue * value,
+ GParamSpec * pspec);
+static void gsm_color_button_realize (GtkWidget * widget);
+static void gsm_color_button_size_request (GtkWidget * widget,
+ GtkRequisition * requisition);
+static void gsm_color_button_size_allocate (GtkWidget * widget,
+ GtkAllocation * allocation);
+static void gsm_color_button_unrealize (GtkWidget * widget);
+static void gsm_color_button_state_changed (GtkWidget * widget,
+ GtkStateType previous_state);
+static void gsm_color_button_style_set (GtkWidget * widget,
+ GtkStyle * previous_style);
+static gint gsm_color_button_clicked (GtkWidget * widget,
+ GdkEventButton * event);
+static gboolean gsm_color_button_enter_notify (GtkWidget * widget,
+ GdkEventCrossing * event);
+static gboolean gsm_color_button_leave_notify (GtkWidget * widget,
+ GdkEventCrossing * event);
+/* source side drag signals */
+static void gsm_color_button_drag_begin (GtkWidget * widget,
+ GdkDragContext * context,
+ gpointer data);
+static void gsm_color_button_drag_data_get (GtkWidget * widget,
+ GdkDragContext * context,
+ GtkSelectionData * selection_data,
+ guint info, guint time,
+ GSMColorButton * color_button);
+
+/* target side drag signals */
+static void gsm_color_button_drag_data_received (GtkWidget * widget,
+ GdkDragContext * context,
+ gint x,
+ gint y,
+ GtkSelectionData *
+ selection_data, guint info,
+ guint32 time,
+ GSMColorButton *
+ color_button);
+
+
+static guint color_button_signals[LAST_SIGNAL] = { 0 };
+
+static gpointer gsm_color_button_parent_class = NULL;
+
+static const GtkTargetEntry drop_types[] = { {"application/x-color", 0, 0} };
+
+GType
+gsm_color_button_get_type ()
+{
+ static GType gsm_color_button_type = 0;
+
+ if (!gsm_color_button_type)
+ {
+ static const GTypeInfo gsm_color_button_info = {
+ sizeof (GSMColorButtonClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gsm_color_button_class_intern_init,
+ NULL,
+ NULL,
+ sizeof (GSMColorButton),
+ 0,
+ (GInstanceInitFunc) gsm_color_button_init,
+ };
+
+ gsm_color_button_type =
+ g_type_register_static (GTK_TYPE_DRAWING_AREA, "GSMColorButton",
+ &gsm_color_button_info, 0);
+ }
+
+ return gsm_color_button_type;
+}
+
+static void
+gsm_color_button_class_intern_init (gpointer klass)
+{
+ gsm_color_button_parent_class = g_type_class_peek_parent (klass);
+ gsm_color_button_class_init ((GSMColorButtonClass *) klass);
+}
+
+static void
+gsm_color_button_class_init (GSMColorButtonClass * klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->get_property = gsm_color_button_get_property;
+ gobject_class->set_property = gsm_color_button_set_property;
+ gobject_class->finalize = gsm_color_button_finalize;
+ widget_class->state_changed = gsm_color_button_state_changed;
+ widget_class->size_request = gsm_color_button_size_request;
+ widget_class->size_allocate = gsm_color_button_size_allocate;
+ widget_class->realize = gsm_color_button_realize;
+ widget_class->unrealize = gsm_color_button_unrealize;
+ widget_class->style_set = gsm_color_button_style_set;
+ widget_class->button_release_event = gsm_color_button_clicked;
+ widget_class->enter_notify_event = gsm_color_button_enter_notify;
+ widget_class->leave_notify_event = gsm_color_button_leave_notify;
+
+ klass->color_set = NULL;
+
+ g_object_class_install_property (gobject_class,
+ PROP_PERCENTAGE,
+ g_param_spec_double ("fraction",
+ _("Fraction"),
+ _("Percentage full for pie colour pickers"),
+ 0, 1, 0.5,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_TITLE,
+ g_param_spec_string ("title",
+ _("Title"),
+ _("The title of the color selection dialog"),
+ _("Pick a Color"),
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_COLOR,
+ g_param_spec_boxed ("color",
+ _("Current Color"),
+ _("The selected color"),
+ GDK_TYPE_COLOR,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_TYPE,
+ g_param_spec_uint ("type", _("Type"),
+ _("Type of color picker"),
+ 0, 4, 0,
+ G_PARAM_READWRITE));
+
+ color_button_signals[COLOR_SET] = g_signal_new ("color_set",
+ G_TYPE_FROM_CLASS
+ (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET
+ (GSMColorButtonClass,
+ color_set), NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (gobject_class, sizeof (GSMColorButtonPrivate));
+}
+
+
+static cairo_surface_t *
+fill_image_buffer_from_file (cairo_t *cr, const char *filePath)
+{
+ GError *error = NULL;
+ RsvgHandle *handle;
+ cairo_surface_t *tmp_surface;
+ cairo_t *tmp_cr;
+
+ handle = rsvg_handle_new_from_file (filePath, &error);
+
+ if (handle == NULL) {
+ g_warning("rsvg_handle_new_from_file(\"%s\") failed: %s",
+ filePath, (error ? error->message : "unknown error"));
+ if (error)
+ g_error_free(error);
+ return NULL;
+ }
+
+ tmp_surface = cairo_surface_create_similar (cairo_get_target (cr),
+ CAIRO_CONTENT_COLOR_ALPHA,
+ 32, 32);
+ tmp_cr = cairo_create (tmp_surface);
+ rsvg_handle_render_cairo (handle, tmp_cr);
+ cairo_destroy (tmp_cr);
+ rsvg_handle_free (handle);
+ return tmp_surface;
+}
+
+
+static void
+render (GtkWidget * widget)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (widget);
+ GdkColor *color, tmp_color = color_button->priv->color;
+ color = &tmp_color;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget));
+ cairo_path_t *path = NULL;
+ gint width, height;
+ gdouble radius, arc_start, arc_end;
+ gint highlight_factor;
+
+ if (color_button->priv->highlight > 0) {
+ highlight_factor = 8192 * color_button->priv->highlight;
+
+ if (color->red + highlight_factor > 65535)
+ color->red = 65535;
+ else
+ color->red = color->red + highlight_factor;
+
+ if (color->blue + highlight_factor > 65535)
+ color->blue = 65535;
+ else
+ color->blue = color->blue + highlight_factor;
+
+ if (color->green + highlight_factor > 65535)
+ color->green = 65535;
+ else
+ color->green = color->green + highlight_factor;
+ }
+ gdk_cairo_set_source_color (cr, color);
+
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ width = gdk_window_get_width(gtk_widget_get_window(widget));
+ height = gdk_window_get_height(gtk_widget_get_window(widget));
+ #else
+ gdk_drawable_get_size(gtk_widget_get_window(widget), &width, &height);
+ #endif
+
+
+ switch (color_button->priv->type)
+ {
+ case GSMCP_TYPE_CPU:
+ //gtk_widget_set_size_request (widget, GSMCP_MIN_WIDTH, GSMCP_MIN_HEIGHT);
+ cairo_paint (cr);
+ cairo_set_line_width (cr, 1);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+ cairo_rectangle (cr, 0.5, 0.5, width - 1, height - 1);
+ cairo_stroke (cr);
+ cairo_set_line_width (cr, 1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.4);
+ cairo_rectangle (cr, 1.5, 1.5, width - 3, height - 3);
+ cairo_stroke (cr);
+ break;
+ case GSMCP_TYPE_PIE:
+ if (width < 32) // 32px minimum size
+ gtk_widget_set_size_request (widget, 32, 32);
+ if (width < height)
+ radius = width / 2;
+ else
+ radius = height / 2;
+
+ arc_start = -G_PI_2 + 2 * G_PI * color_button->priv->fraction;
+ arc_end = -G_PI_2;
+
+ cairo_set_line_width (cr, 1);
+
+ // Draw external stroke and fill
+ if (color_button->priv->fraction < 0.01) {
+ cairo_arc (cr, (width / 2) + .5, (height / 2) + .5, 4.5,
+ 0, 2 * G_PI);
+ } else if (color_button->priv->fraction > 0.99) {
+ cairo_arc (cr, (width / 2) + .5, (height / 2) + .5, radius - 2.25,
+ 0, 2 * G_PI);
+ } else {
+ cairo_arc_negative (cr, (width / 2) + .5, (height / 2) + .5, radius - 2.25,
+ arc_start, arc_end);
+ cairo_arc_negative (cr, (width / 2) + .5, (height / 2) + .5, 4.5,
+ arc_end, arc_start);
+ cairo_arc_negative (cr, (width / 2) + .5, (height / 2) + .5, radius - 2.25,
+ arc_start, arc_start);
+ }
+ cairo_fill_preserve (cr);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.7);
+ cairo_stroke (cr);
+
+ // Draw internal highlight
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.45);
+ cairo_set_line_width (cr, 1);
+
+ if (color_button->priv->fraction < 0.03) {
+ cairo_arc (cr, (width / 2) + .5, (height / 2) + .5, 3.25,
+ 0, 2 * G_PI);
+ } else if (color_button->priv->fraction > 0.99) {
+ cairo_arc (cr, (width / 2) + .5, (height / 2) + .5, radius - 3.5,
+ 0, 2 * G_PI);
+ } else {
+ cairo_arc_negative (cr, (width / 2) + .5, (height / 2) + .5, radius - 3.5,
+ arc_start + (1 / (radius - 3.75)),
+ arc_end - (1 / (radius - 3.75)));
+ cairo_arc_negative (cr, (width / 2) + .5, (height / 2) + .5, 3.25,
+ arc_end - (1 / (radius - 3.75)),
+ arc_start + (1 / (radius - 3.75)));
+ cairo_arc_negative (cr, (width / 2) + .5, (height / 2) + .5, radius - 3.5,
+ arc_start + (1 / (radius - 3.75)),
+ arc_start + (1 / (radius - 3.75)));
+ }
+ cairo_stroke (cr);
+
+ // Draw external shape
+ cairo_set_line_width (cr, 1);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
+ cairo_arc (cr, (width / 2) + .5, (height / 2) + .5, radius - 1.25, 0,
+ G_PI * 2);
+ cairo_stroke (cr);
+
+ break;
+ case GSMCP_TYPE_NETWORK_IN:
+ if (color_button->priv->image_buffer == NULL)
+ color_button->priv->image_buffer =
+ fill_image_buffer_from_file (cr, DATADIR "/pixmaps/mate-system-monitor/download.svg");
+ gtk_widget_set_size_request (widget, 32, 32);
+ cairo_move_to (cr, 8.5, 1.5);
+ cairo_line_to (cr, 23.5, 1.5);
+ cairo_line_to (cr, 23.5, 11.5);
+ cairo_line_to (cr, 29.5, 11.5);
+ cairo_line_to (cr, 16.5, 27.5);
+ cairo_line_to (cr, 15.5, 27.5);
+ cairo_line_to (cr, 2.5, 11.5);
+ cairo_line_to (cr, 8.5, 11.5);
+ cairo_line_to (cr, 8.5, 1.5);
+ cairo_close_path (cr);
+ path = cairo_copy_path (cr);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+ cairo_set_line_width (cr, 1);
+ cairo_fill_preserve (cr);
+ cairo_set_miter_limit (cr, 5.0);
+ cairo_stroke (cr);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+ cairo_append_path (cr, path);
+ cairo_path_destroy(path);
+ cairo_stroke (cr);
+ cairo_set_source_surface (cr, color_button->priv->image_buffer, 0.0,
+ 0.0);
+ cairo_paint (cr);
+
+ break;
+ case GSMCP_TYPE_NETWORK_OUT:
+ if (color_button->priv->image_buffer == NULL)
+ color_button->priv->image_buffer =
+ fill_image_buffer_from_file (cr, DATADIR "/pixmaps/mate-system-monitor/upload.svg");
+ gtk_widget_set_size_request (widget, 32, 32);
+ cairo_move_to (cr, 16.5, 1.5);
+ cairo_line_to (cr, 29.5, 17.5);
+ cairo_line_to (cr, 23.5, 17.5);
+ cairo_line_to (cr, 23.5, 27.5);
+ cairo_line_to (cr, 8.5, 27.5);
+ cairo_line_to (cr, 8.5, 17.5);
+ cairo_line_to (cr, 2.5, 17.5);
+ cairo_line_to (cr, 15.5, 1.5);
+ cairo_line_to (cr, 16.5, 1.5);
+ cairo_close_path (cr);
+ path = cairo_copy_path (cr);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+ cairo_set_line_width (cr, 1);
+ cairo_fill_preserve (cr);
+ cairo_set_miter_limit (cr, 5.0);
+ cairo_stroke (cr);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+ cairo_append_path (cr, path);
+ cairo_path_destroy(path);
+ cairo_stroke (cr);
+ cairo_set_source_surface (cr, color_button->priv->image_buffer, 0.0,
+ 0.0);
+ cairo_paint (cr);
+
+ break;
+ }
+ cairo_destroy (cr);
+}
+
+/* Handle exposure events for the color picker's drawing area */
+static gint
+expose_event (GtkWidget * widget, GdkEventExpose * event, gpointer data)
+{
+ render (GTK_WIDGET (data));
+
+ return FALSE;
+}
+
+static void
+gsm_color_button_realize (GtkWidget * widget)
+{
+ GTK_WIDGET_CLASS (gsm_color_button_parent_class)->realize (widget);
+ render (widget);
+}
+
+static void
+gsm_color_button_size_request (GtkWidget * widget,
+ GtkRequisition * requisition)
+{
+ g_return_if_fail (widget != NULL || requisition != NULL);
+ g_return_if_fail (GSM_IS_COLOR_BUTTON (widget));
+
+ requisition->width = GSMCP_MIN_WIDTH;
+ requisition->height = GSMCP_MIN_HEIGHT;
+}
+
+static void
+gsm_color_button_size_allocate (GtkWidget * widget,
+ GtkAllocation * allocation)
+{
+ GSMColorButton *color_button;
+
+ g_return_if_fail (widget != NULL || allocation != NULL);
+ g_return_if_fail (GSM_IS_COLOR_BUTTON (widget));
+
+ gtk_widget_set_allocation (widget, allocation);
+ color_button = GSM_COLOR_BUTTON (widget);
+
+ if (gtk_widget_get_realized (widget))
+ {
+ gdk_window_move_resize (gtk_widget_get_window (widget), allocation->x, allocation->y,
+ allocation->width, allocation->height);
+ }
+}
+
+static void
+gsm_color_button_unrealize (GtkWidget * widget)
+{
+
+ GTK_WIDGET_CLASS (gsm_color_button_parent_class)->unrealize (widget);
+}
+
+static void
+gsm_color_button_style_set (GtkWidget * widget, GtkStyle * previous_style)
+{
+
+ GTK_WIDGET_CLASS (gsm_color_button_parent_class)->style_set (widget,
+ previous_style);
+
+}
+
+static void
+gsm_color_button_state_changed (GtkWidget * widget,
+ GtkStateType previous_state)
+{
+}
+
+static void
+gsm_color_button_drag_data_received (GtkWidget * widget,
+ GdkDragContext * context,
+ gint x,
+ gint y,
+ GtkSelectionData * selection_data,
+ guint info,
+ guint32 time,
+ GSMColorButton * color_button)
+{
+ gint length;
+ guint16 *dropped;
+
+ length = gtk_selection_data_get_length (selection_data);
+
+ if (length < 0)
+ return;
+
+ /* We accept drops with the wrong format, since the KDE color
+ * chooser incorrectly drops application/x-color with format 8.
+ */
+ if (length != 8)
+ {
+ g_warning (_("Received invalid color data\n"));
+ return;
+ }
+
+
+ dropped = (guint16 *) gtk_selection_data_get_data (selection_data);
+
+ color_button->priv->color.red = dropped[0];
+ color_button->priv->color.green = dropped[1];
+ color_button->priv->color.blue = dropped[2];
+
+ gtk_widget_queue_draw (GTK_WIDGET (&color_button->widget));
+
+ g_signal_emit (color_button, color_button_signals[COLOR_SET], 0);
+
+ g_object_freeze_notify (G_OBJECT (color_button));
+ g_object_notify (G_OBJECT (color_button), "color");
+ g_object_thaw_notify (G_OBJECT (color_button));
+}
+
+
+static void
+set_color_icon (GdkDragContext * context, GdkColor * color)
+{
+ GdkPixbuf *pixbuf;
+ guint32 pixel;
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 48, 32);
+
+ pixel = ((color->red & 0xff00) << 16) |
+ ((color->green & 0xff00) << 8) | (color->blue & 0xff00);
+
+ gdk_pixbuf_fill (pixbuf, pixel);
+
+ gtk_drag_set_icon_pixbuf (context, pixbuf, -2, -2);
+ g_object_unref (pixbuf);
+}
+
+static void
+gsm_color_button_drag_begin (GtkWidget * widget,
+ GdkDragContext * context, gpointer data)
+{
+ GSMColorButton *color_button = data;
+
+ set_color_icon (context, &color_button->priv->color);
+}
+
+static void
+gsm_color_button_drag_data_get (GtkWidget * widget,
+ GdkDragContext * context,
+ GtkSelectionData * selection_data,
+ guint info,
+ guint time, GSMColorButton * color_button)
+{
+ guint16 dropped[4];
+
+ dropped[0] = color_button->priv->color.red;
+ dropped[1] = color_button->priv->color.green;
+ dropped[2] = color_button->priv->color.blue;
+ dropped[3] = 65535; // This widget doesn't care about alpha
+
+ gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
+ 16, (guchar *) dropped, 8);
+}
+
+
+static void
+gsm_color_button_init (GSMColorButton * color_button)
+{
+ color_button->priv = GSM_COLOR_BUTTON_GET_PRIVATE (color_button);
+
+ rsvg_init ();
+
+ color_button->priv->color.red = 0;
+ color_button->priv->color.green = 0;
+ color_button->priv->color.blue = 0;
+ color_button->priv->fraction = 0.5;
+ color_button->priv->type = GSMCP_TYPE_CPU;
+ color_button->priv->image_buffer = NULL;
+ color_button->priv->title = g_strdup (_("Pick a Color")); /* default title */
+
+ gtk_drag_dest_set (GTK_WIDGET (color_button),
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_DROP, drop_types, 1, GDK_ACTION_COPY);
+ gtk_drag_source_set (GTK_WIDGET (color_button),
+ GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+ drop_types, 1, GDK_ACTION_COPY);
+ g_signal_connect (color_button, "drag_begin",
+ G_CALLBACK (gsm_color_button_drag_begin), color_button);
+ g_signal_connect (color_button, "drag_data_received",
+ G_CALLBACK (gsm_color_button_drag_data_received),
+ color_button);
+ g_signal_connect (color_button, "drag_data_get",
+ G_CALLBACK (gsm_color_button_drag_data_get),
+ color_button);
+
+ gtk_widget_add_events (GTK_WIDGET(color_button), GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK);
+
+ gtk_widget_set_tooltip_text (GTK_WIDGET(color_button), _("Click to set graph colors"));
+
+ g_signal_connect (color_button, "expose-event",
+ G_CALLBACK (expose_event), color_button);
+}
+
+static void
+gsm_color_button_finalize (GObject * object)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (object);
+
+ if (color_button->priv->cs_dialog != NULL)
+ gtk_widget_destroy (color_button->priv->cs_dialog);
+ color_button->priv->cs_dialog = NULL;
+
+ g_free (color_button->priv->title);
+ color_button->priv->title = NULL;
+
+ cairo_surface_destroy (color_button->priv->image_buffer);
+ color_button->priv->image_buffer = NULL;
+
+ rsvg_term ();
+ G_OBJECT_CLASS (gsm_color_button_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gsm_color_button_new (const GdkColor * color, guint type)
+{
+ return g_object_new (GSM_TYPE_COLOR_BUTTON, "color", color, "type", type,
+ NULL);
+}
+
+static void
+dialog_response (GtkWidget * widget, GtkResponseType response, gpointer data)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (data);
+ GtkColorSelection *color_selection;
+
+ if (response == GTK_RESPONSE_OK) {
+ color_selection =
+ GTK_COLOR_SELECTION (gtk_color_selection_dialog_get_color_selection (GTK_COLOR_SELECTION_DIALOG
+ (color_button->priv->cs_dialog)));
+
+ gtk_color_selection_get_current_color (color_selection,
+ &color_button->priv->color);
+
+ gtk_widget_hide (color_button->priv->cs_dialog);
+
+ gtk_widget_queue_draw (GTK_WIDGET (&color_button->widget));
+
+ g_signal_emit (color_button, color_button_signals[COLOR_SET], 0);
+
+ g_object_freeze_notify (G_OBJECT (color_button));
+ g_object_notify (G_OBJECT (color_button), "color");
+ g_object_thaw_notify (G_OBJECT (color_button));
+ }
+ else /* (response == GTK_RESPONSE_CANCEL) */
+ gtk_widget_hide (color_button->priv->cs_dialog);
+}
+
+static gboolean
+dialog_destroy (GtkWidget * widget, gpointer data)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (data);
+
+ color_button->priv->cs_dialog = NULL;
+
+ return FALSE;
+}
+
+static gint
+gsm_color_button_clicked (GtkWidget * widget, GdkEventButton * event)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (widget);
+ GtkColorSelectionDialog *color_dialog;
+
+ /* if dialog already exists, make sure it's shown and raised */
+ if (!color_button->priv->cs_dialog)
+ {
+ /* Create the dialog and connects its buttons */
+ GtkWidget *parent;
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (color_button));
+
+ color_button->priv->cs_dialog =
+ gtk_color_selection_dialog_new (color_button->priv->title);
+
+ color_dialog =
+ GTK_COLOR_SELECTION_DIALOG (color_button->priv->cs_dialog);
+
+ if (gtk_widget_is_toplevel (parent) && GTK_IS_WINDOW (parent))
+ {
+ if (GTK_WINDOW (parent) !=
+ gtk_window_get_transient_for (GTK_WINDOW (color_dialog)))
+ gtk_window_set_transient_for (GTK_WINDOW (color_dialog),
+ GTK_WINDOW (parent));
+
+ gtk_window_set_modal (GTK_WINDOW (color_dialog),
+ gtk_window_get_modal (GTK_WINDOW (parent)));
+ }
+
+ g_signal_connect (color_dialog, "response",
+ G_CALLBACK (dialog_response), color_button);
+
+ g_signal_connect (color_dialog, "destroy",
+ G_CALLBACK (dialog_destroy), color_button);
+ }
+
+ color_dialog = GTK_COLOR_SELECTION_DIALOG (color_button->priv->cs_dialog);
+
+ gtk_color_selection_set_previous_color (GTK_COLOR_SELECTION
+ (gtk_color_selection_dialog_get_color_selection (color_dialog)),
+ &color_button->priv->color);
+
+ gtk_color_selection_set_current_color (GTK_COLOR_SELECTION
+ (gtk_color_selection_dialog_get_color_selection (color_dialog)),
+ &color_button->priv->color);
+
+ gtk_window_present (GTK_WINDOW (color_button->priv->cs_dialog));
+ return 0;
+}
+
+static gboolean
+gsm_color_button_enter_notify (GtkWidget * widget, GdkEventCrossing * event)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (widget);
+ color_button->priv->highlight = 1.0;
+ gtk_widget_queue_draw(widget);
+ return FALSE;
+}
+
+static gboolean
+gsm_color_button_leave_notify (GtkWidget * widget, GdkEventCrossing * event)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (widget);
+ color_button->priv->highlight = 0;
+ gtk_widget_queue_draw(widget);
+ return FALSE;
+}
+
+guint
+gsm_color_button_get_cbtype (GSMColorButton * color_button)
+{
+ g_return_val_if_fail (GSM_IS_COLOR_BUTTON (color_button), 0);
+
+ return color_button->priv->type;
+}
+
+void
+gsm_color_button_set_cbtype (GSMColorButton * color_button, guint type)
+{
+ g_return_if_fail (GSM_IS_COLOR_BUTTON (color_button));
+
+ color_button->priv->type = type;
+
+ gtk_widget_queue_draw (GTK_WIDGET (&color_button->widget));
+
+ g_object_notify (G_OBJECT (color_button), "type");
+}
+
+gdouble
+gsm_color_button_get_fraction (GSMColorButton * color_button)
+{
+ g_return_val_if_fail (GSM_IS_COLOR_BUTTON (color_button), 0);
+
+ return color_button->priv->fraction;
+}
+
+void
+gsm_color_button_set_fraction (GSMColorButton * color_button,
+ gdouble fraction)
+{
+ g_return_if_fail (GSM_IS_COLOR_BUTTON (color_button));
+
+ color_button->priv->fraction = fraction;
+
+ gtk_widget_queue_draw (GTK_WIDGET (&color_button->widget));
+
+ g_object_notify (G_OBJECT (color_button), "fraction");
+}
+
+void
+gsm_color_button_get_color (GSMColorButton * color_button, GdkColor * color)
+{
+ g_return_if_fail (GSM_IS_COLOR_BUTTON (color_button));
+
+ color->red = color_button->priv->color.red;
+ color->green = color_button->priv->color.green;
+ color->blue = color_button->priv->color.blue;
+}
+
+void
+gsm_color_button_set_color (GSMColorButton * color_button,
+ const GdkColor * color)
+{
+ g_return_if_fail (GSM_IS_COLOR_BUTTON (color_button));
+ g_return_if_fail (color != NULL);
+
+ color_button->priv->color.red = color->red;
+ color_button->priv->color.green = color->green;
+ color_button->priv->color.blue = color->blue;
+
+ gtk_widget_queue_draw (GTK_WIDGET (&color_button->widget)); //->priv->draw_area);
+
+ g_object_notify (G_OBJECT (color_button), "color");
+}
+
+void
+gsm_color_button_set_title (GSMColorButton * color_button,
+ const gchar * title)
+{
+ gchar *old_title;
+
+ g_return_if_fail (GSM_IS_COLOR_BUTTON (color_button));
+
+ old_title = color_button->priv->title;
+ color_button->priv->title = g_strdup (title);
+ g_free (old_title);
+
+ if (color_button->priv->cs_dialog)
+ gtk_window_set_title (GTK_WINDOW (color_button->priv->cs_dialog),
+ color_button->priv->title);
+
+ g_object_notify (G_OBJECT (color_button), "title");
+}
+
+const gchar* gsm_color_button_get_title(GSMColorButton* color_button)
+{
+ g_return_val_if_fail(GSM_IS_COLOR_BUTTON(color_button), NULL);
+
+ return color_button->priv->title;
+}
+
+static void
+gsm_color_button_set_property (GObject * object,
+ guint param_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (object);
+
+ switch (param_id)
+ {
+ case PROP_PERCENTAGE:
+ gsm_color_button_set_fraction (color_button,
+ g_value_get_double (value));
+ break;
+ case PROP_TITLE:
+ gsm_color_button_set_title (color_button, g_value_get_string (value));
+ break;
+ case PROP_COLOR:
+ gsm_color_button_set_color (color_button, g_value_get_boxed (value));
+ break;
+ case PROP_TYPE:
+ gsm_color_button_set_cbtype (color_button, g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gsm_color_button_get_property (GObject * object,
+ guint param_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GSMColorButton *color_button = GSM_COLOR_BUTTON (object);
+ GdkColor color;
+
+ switch (param_id)
+ {
+ case PROP_PERCENTAGE:
+ g_value_set_double (value,
+ gsm_color_button_get_fraction (color_button));
+ break;
+ case PROP_TITLE:
+ g_value_set_string (value, gsm_color_button_get_title (color_button));
+ break;
+ case PROP_COLOR:
+ gsm_color_button_get_color (color_button, &color);
+ g_value_set_boxed (value, &color);
+ break;
+ case PROP_TYPE:
+ g_value_set_uint (value, gsm_color_button_get_cbtype (color_button));
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+#define __GSM_COLOR_BUTTON_C__
diff --git a/src/gsm_color_button.h b/src/gsm_color_button.h
new file mode 100644
index 0000000..5effbc8
--- /dev/null
+++ b/src/gsm_color_button.h
@@ -0,0 +1,92 @@
+/*
+ * Mate system monitor colour pickers
+ * Copyright (C) 2007 Karl Lattimer <[email protected]>
+ * All rights reserved.
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with the software; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSM_COLOR_BUTTON_H__
+#define __GSM_COLOR_BUTTON_H__
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <cairo.h>
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+
+G_BEGIN_DECLS
+/* The GtkColorSelectionButton widget is a simple color picker in a button.
+ * The button displays a sample of the currently selected color. When
+ * the user clicks on the button, a color selection dialog pops up.
+ * The color picker emits the "color_set" signal when the color is set.
+ */
+#define GSM_TYPE_COLOR_BUTTON (gsm_color_button_get_type ())
+#define GSM_COLOR_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_COLOR_BUTTON, GSMColorButton))
+#define GSM_COLOR_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_COLOR_BUTTON, GSMColorButtonClass))
+#define GSM_IS_COLOR_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_COLOR_BUTTON))
+#define GSM_IS_COLOR_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_COLOR_BUTTON))
+#define GSM_COLOR_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_COLOR_BUTTON, GSMColorButtonClass))
+typedef struct _GSMColorButton GSMColorButton;
+typedef struct _GSMColorButtonClass GSMColorButtonClass;
+typedef struct _GSMColorButtonPrivate GSMColorButtonPrivate;
+
+struct _GSMColorButton
+{
+ GtkDrawingArea widget;
+
+ /*< private > */
+
+ GSMColorButtonPrivate *priv;
+};
+
+/* Widget types */
+enum
+{
+ GSMCP_TYPE_CPU,
+ GSMCP_TYPE_PIE,
+ GSMCP_TYPE_NETWORK_IN,
+ GSMCP_TYPE_NETWORK_OUT,
+ GSMCP_TYPES
+};
+
+struct _GSMColorButtonClass
+{
+ GtkWidgetClass parent_class;
+
+ void (*color_set) (GSMColorButton * cp);
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+};
+
+GType gsm_color_button_get_type (void) G_GNUC_CONST;
+GtkWidget *gsm_color_button_new (const GdkColor * color, guint type);
+void gsm_color_button_set_color (GSMColorButton * color_button, const GdkColor * color);
+void gsm_color_button_set_fraction (GSMColorButton * color_button, const gdouble fraction);
+void gsm_color_button_set_cbtype (GSMColorButton * color_button, guint type);
+void gsm_color_button_get_color (GSMColorButton * color_button, GdkColor * color);
+gdouble gsm_color_button_get_fraction (GSMColorButton * color_button);
+guint gsm_color_button_get_cbtype (GSMColorButton * color_button);
+void gsm_color_button_set_title (GSMColorButton * color_button, const gchar * title);
+const gchar* gsm_color_button_get_title(GSMColorButton* color_button);
+
+G_END_DECLS
+#endif /* __GSM_COLOR_BUTTON_H__ */
diff --git a/src/iconthemewrapper.cpp b/src/iconthemewrapper.cpp
new file mode 100644
index 0000000..496dd45
--- /dev/null
+++ b/src/iconthemewrapper.cpp
@@ -0,0 +1,23 @@
+#include <config.h>
+
+#include <gtkmm/icontheme.h>
+
+#include "iconthemewrapper.h"
+
+
+Glib::RefPtr<Gdk::Pixbuf>
+procman::IconThemeWrapper::load_icon(const Glib::ustring& icon_name,
+ int size, Gtk::IconLookupFlags flags) const
+{
+ try
+ {
+ return Gtk::IconTheme::get_default()->load_icon(icon_name, size, flags);
+ }
+ catch (Gtk::IconThemeError &error)
+ {
+ if (error.code() != Gtk::IconThemeError::ICON_THEME_NOT_FOUND)
+ g_error("Cannot load icon '%s' from theme: %s", icon_name.c_str(), error.what().c_str());
+ return Glib::RefPtr<Gdk::Pixbuf>();
+ }
+}
+
diff --git a/src/iconthemewrapper.h b/src/iconthemewrapper.h
new file mode 100644
index 0000000..6127f01
--- /dev/null
+++ b/src/iconthemewrapper.h
@@ -0,0 +1,23 @@
+#ifndef H_PROCMAN_ICONTHEMEWRAPPER_H_1185707711
+#define H_PROCMAN_ICONTHEMEWRAPPER_H_1185707711
+
+#include <glibmm/refptr.h>
+#include <glibmm/ustring.h>
+#include <gtkmm/icontheme.h>
+#include <gdkmm/pixbuf.h>
+
+namespace procman
+{
+ class IconThemeWrapper
+ {
+ public:
+ // returns 0 instead of raising an exception
+ Glib::RefPtr<Gdk::Pixbuf>
+ load_icon(const Glib::ustring& icon_name, int size, Gtk::IconLookupFlags flags) const;
+
+ const IconThemeWrapper* operator->() const
+ { return this; }
+ };
+}
+
+#endif // H_PROCMAN_ICONTHEMEWRAPPER_H_1185707711
diff --git a/src/interface.cpp b/src/interface.cpp
new file mode 100644
index 0000000..2a88676
--- /dev/null
+++ b/src/interface.cpp
@@ -0,0 +1,808 @@
+/* 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.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 "procman.h"
+#include "callbacks.h"
+#include "interface.h"
+#include "proctable.h"
+#include "procactions.h"
+#include "load-graph.h"
+#include "util.h"
+#include "disks.h"
+#include "sysinfo.h"
+#include "gsm_color_button.h"
+
+static void cb_toggle_tree (GtkAction *action, gpointer data);
+
+static const GtkActionEntry menu_entries[] =
+{
+ // xgettext: noun, top level menu.
+ // "File" did not make sense for system-monitor
+ { "Monitor", NULL, N_("_Monitor") },
+ { "Edit", NULL, N_("_Edit") },
+ { "View", NULL, N_("_View") },
+ { "Help", NULL, N_("_Help") },
+
+ { "Lsof", GTK_STOCK_FIND, N_("Search for _Open Files"), "<control>O",
+ N_("Search for open files"), G_CALLBACK(cb_show_lsof) },
+ { "Quit", GTK_STOCK_QUIT, NULL, NULL,
+ N_("Quit the program"), G_CALLBACK (cb_app_exit) },
+
+
+ { "StopProcess", NULL, N_("_Stop Process"), "<control>S",
+ N_("Stop process"), G_CALLBACK(cb_kill_sigstop) },
+ { "ContProcess", NULL, N_("_Continue Process"), "<control>C",
+ N_("Continue process if stopped"), G_CALLBACK(cb_kill_sigcont) },
+
+ { "EndProcess", NULL, N_("_End Process"), "<control>E",
+ N_("Force process to finish normally"), G_CALLBACK (cb_end_process) },
+ { "KillProcess", NULL, N_("_Kill Process"), "<control>K",
+ N_("Force process to finish immediately"), G_CALLBACK (cb_kill_process) },
+ { "ChangePriority", NULL, N_("_Change Priority..."), "<control>N",
+ N_("Change the order of priority of process"), G_CALLBACK (cb_renice) },
+ { "Preferences", GTK_STOCK_PREFERENCES, NULL, NULL,
+ N_("Configure the application"), G_CALLBACK (cb_edit_preferences) },
+
+ { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "<control>R",
+ N_("Refresh the process list"), G_CALLBACK(cb_user_refresh) },
+
+ { "MemoryMaps", NULL, N_("_Memory Maps"), "<control>M",
+ N_("Open the memory maps associated with a process"), G_CALLBACK (cb_show_memory_maps) },
+ { "OpenFiles", NULL, N_("Open _Files"), "<control>F",
+ N_("View the files opened by a process"), G_CALLBACK (cb_show_open_files) },
+
+ { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1",
+ N_("Open the manual"), G_CALLBACK (cb_help_contents) },
+ { "About", GTK_STOCK_ABOUT, NULL, NULL,
+ N_("About this application"), G_CALLBACK (cb_about) }
+};
+
+static const GtkToggleActionEntry toggle_menu_entries[] =
+{
+ { "ShowDependencies", NULL, N_("_Dependencies"), "<control>D",
+ N_("Show parent/child relationship between processes"),
+ G_CALLBACK (cb_toggle_tree), TRUE },
+};
+
+
+static const GtkRadioActionEntry radio_menu_entries[] =
+{
+ { "ShowActiveProcesses", NULL, N_("_Active Processes"), NULL,
+ N_("Show active processes"), ACTIVE_PROCESSES },
+ { "ShowAllProcesses", NULL, N_("A_ll Processes"), NULL,
+ N_("Show all processes"), ALL_PROCESSES },
+ { "ShowMyProcesses", NULL, N_("M_y Processes"), NULL,
+ N_("Show user own process"), MY_PROCESSES }
+};
+
+
+static const char ui_info[] =
+" <menubar name=\"MenuBar\">"
+" <menu name=\"MonitorMenu\" action=\"Monitor\">"
+" <menuitem name=\"MonitorLsofMenu\" action=\"Lsof\" />"
+" <menuitem name=\"MonitorQuitMenu\" action=\"Quit\" />"
+" </menu>"
+" <menu name=\"EditMenu\" action=\"Edit\">"
+" <menuitem name=\"EditStopProcessMenu\" action=\"StopProcess\" />"
+" <menuitem name=\"EditContProcessMenu\" action=\"ContProcess\" />"
+" <separator />"
+" <menuitem name=\"EditEndProcessMenu\" action=\"EndProcess\" />"
+" <menuitem name=\"EditKillProcessMenu\" action=\"KillProcess\" />"
+" <separator />"
+" <menuitem name=\"EditChangePriorityMenu\" action=\"ChangePriority\" />"
+" <separator />"
+" <menuitem name=\"EditPreferencesMenu\" action=\"Preferences\" />"
+" </menu>"
+" <menu name=\"ViewMenu\" action=\"View\">"
+" <menuitem name=\"ViewActiveProcesses\" action=\"ShowActiveProcesses\" />"
+" <menuitem name=\"ViewAllProcesses\" action=\"ShowAllProcesses\" />"
+" <menuitem name=\"ViewMyProcesses\" action=\"ShowMyProcesses\" />"
+" <separator />"
+" <menuitem name=\"ViewDependenciesMenu\" action=\"ShowDependencies\" />"
+" <separator />"
+" <menuitem name=\"ViewMemoryMapsMenu\" action=\"MemoryMaps\" />"
+" <menuitem name=\"ViewOpenFilesMenu\" action=\"OpenFiles\" />"
+" <separator />"
+" <menuitem name=\"ViewRefresh\" action=\"Refresh\" />"
+" </menu>"
+" <menu name=\"HelpMenu\" action=\"Help\">"
+" <menuitem name=\"HelpContentsMenu\" action=\"HelpContents\" />"
+" <menuitem name=\"HelpAboutMenu\" action=\"About\" />"
+" </menu>"
+" </menubar>"
+" <popup name=\"PopupMenu\" action=\"Popup\">"
+" <menuitem action=\"StopProcess\" />"
+" <menuitem action=\"ContProcess\" />"
+" <separator />"
+" <menuitem action=\"EndProcess\" />"
+" <menuitem action=\"KillProcess\" />"
+" <separator />"
+" <menuitem action=\"ChangePriority\" />"
+" <separator />"
+" <menuitem action=\"MemoryMaps\" />"
+" <menuitem action=\"OpenFiles\" />"
+" </popup>";
+
+
+static GtkWidget *
+create_proc_view (ProcData *procdata)
+{
+ GtkWidget *vbox1;
+ GtkWidget *hbox1;
+ GtkWidget *scrolled;
+ GtkWidget *hbox2;
+ char* string;
+
+ vbox1 = gtk_vbox_new (FALSE, 18);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox1), 12);
+
+ hbox1 = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0);
+
+ string = make_loadavg_string ();
+ procdata->loadavg = gtk_label_new (string);
+ g_free (string);
+ gtk_box_pack_start (GTK_BOX (hbox1), procdata->loadavg, FALSE, FALSE, 0);
+
+
+ scrolled = proctable_new (procdata);
+ if (!scrolled)
+ return NULL;
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_SHADOW_IN);
+
+ gtk_box_pack_start (GTK_BOX (vbox1), scrolled, TRUE, TRUE, 0);
+
+
+ hbox2 = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox1), hbox2, FALSE, FALSE, 0);
+
+ procdata->endprocessbutton = gtk_button_new_with_mnemonic (_("End _Process"));
+ gtk_box_pack_end (GTK_BOX (hbox2), procdata->endprocessbutton, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (procdata->endprocessbutton), "clicked",
+ G_CALLBACK (cb_end_process_button_pressed), procdata);
+
+
+ /* create popup_menu */
+ procdata->popup_menu = gtk_ui_manager_get_widget (procdata->uimanager, "/PopupMenu");
+
+ return vbox1;
+}
+
+
+GtkWidget *
+make_title_label (const char *text)
+{
+ GtkWidget *label;
+ char *full;
+
+ full = g_strdup_printf ("<span weight=\"bold\">%s</span>", text);
+ label = gtk_label_new (full);
+ g_free (full);
+
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0f, 0.5f);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+
+ return label;
+}
+
+
+static GtkWidget *
+create_sys_view (ProcData *procdata)
+{
+ GtkWidget *vbox, *hbox;
+ GtkWidget *cpu_box, *mem_box, *net_box;
+ GtkWidget *cpu_graph_box, *mem_graph_box, *net_graph_box;
+ GtkWidget *label,*cpu_label, *spacer;
+ GtkWidget *table;
+ GtkWidget *color_picker;
+ GtkWidget *mem_legend_box, *net_legend_box;
+ GtkSizeGroup *sizegroup;
+ LoadGraph *cpu_graph, *mem_graph, *net_graph;
+ gint i;
+
+
+ vbox = gtk_vbox_new (FALSE, 18);
+
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ /* The CPU BOX */
+
+ cpu_box = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), cpu_box, TRUE, TRUE, 0);
+
+ label = make_title_label (_("CPU History"));
+ gtk_box_pack_start (GTK_BOX (cpu_box), label, FALSE, FALSE, 0);
+
+ cpu_graph_box = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (cpu_box), cpu_graph_box, TRUE, TRUE, 0);
+
+ cpu_graph = new LoadGraph(LOAD_GRAPH_CPU);
+ gtk_box_pack_start (GTK_BOX (cpu_graph_box),
+ load_graph_get_widget(cpu_graph),
+ TRUE,
+ TRUE,
+ 0);
+
+ sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ spacer = gtk_label_new ("");
+ gtk_widget_set_size_request(GTK_WIDGET(spacer), 57, -1);
+ gtk_box_pack_start (GTK_BOX (hbox), spacer,
+ FALSE, FALSE, 0);
+
+
+ gtk_box_pack_start (GTK_BOX (cpu_graph_box), hbox,
+ FALSE, FALSE, 0);
+
+ /*cpu_legend_box = gtk_hbox_new(TRUE, 10);
+ gtk_box_pack_start (GTK_BOX (hbox), cpu_legend_box,
+ TRUE, TRUE, 0);*/
+
+ GtkWidget* cpu_table = gtk_table_new(std::min(procdata->config.num_cpus / 4, 1),
+ std::min(procdata->config.num_cpus, 4),
+ TRUE);
+ gtk_table_set_row_spacings(GTK_TABLE(cpu_table), 6);
+ gtk_table_set_col_spacings(GTK_TABLE(cpu_table), 6);
+ gtk_box_pack_start(GTK_BOX(hbox), cpu_table, TRUE, TRUE, 0);
+
+ for (i=0;i<procdata->config.num_cpus; i++) {
+ GtkWidget *temp_hbox;
+ gchar *text;
+
+ temp_hbox = gtk_hbox_new (FALSE, 0);
+ gtk_table_attach(GTK_TABLE(cpu_table), temp_hbox,
+ i % 4, i % 4 + 1,
+ i / 4, i / 4 + 1,
+ static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL),
+ static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL),
+ 0, 0);
+ //gtk_size_group_add_widget (sizegroup, temp_hbox);
+ /*g_signal_connect (G_OBJECT (temp_hbox), "size_request",
+ G_CALLBACK(size_request), &cpu_size);
+*/
+ 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 (GTK_BOX (temp_hbox), color_picker, FALSE, TRUE, 0);
+ gtk_widget_set_size_request(GTK_WIDGET(color_picker), 32, -1);
+ if(procdata->config.num_cpus == 1) {
+ text = g_strdup (_("CPU"));
+ } else {
+ text = g_strdup_printf (_("CPU%d"), i+1);
+ }
+ label = gtk_label_new (text);
+ gtk_box_pack_start (GTK_BOX (temp_hbox), label, FALSE, FALSE, 6);
+ g_free (text);
+
+ cpu_label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (cpu_label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (temp_hbox), cpu_label, TRUE, TRUE, 0);
+ load_graph_get_labels(cpu_graph)->cpu[i] = cpu_label;
+
+ }
+
+ procdata->cpu_graph = cpu_graph;
+
+ mem_box = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), mem_box, TRUE, TRUE, 0);
+
+ label = make_title_label (_("Memory and Swap History"));
+ gtk_box_pack_start (GTK_BOX (mem_box), label, FALSE, FALSE, 0);
+
+ mem_graph_box = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (mem_box), mem_graph_box, TRUE, TRUE, 0);
+
+
+ mem_graph = new LoadGraph(LOAD_GRAPH_MEM);
+ gtk_box_pack_start (GTK_BOX (mem_graph_box),
+ load_graph_get_widget(mem_graph),
+ TRUE,
+ TRUE,
+ 0);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ spacer = gtk_label_new ("");
+ gtk_widget_set_size_request(GTK_WIDGET(spacer), 54, -1);
+ gtk_box_pack_start (GTK_BOX (hbox), spacer,
+ FALSE, FALSE, 0);
+
+
+ gtk_box_pack_start (GTK_BOX (mem_graph_box), hbox,
+ FALSE, FALSE, 0);
+
+ mem_legend_box = gtk_hbox_new(TRUE, 10);
+ gtk_box_pack_start (GTK_BOX (hbox), mem_legend_box,
+ TRUE, TRUE, 0);
+
+ table = gtk_table_new (2, 7, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (mem_legend_box), table,
+ TRUE, TRUE, 0);
+
+ 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), procdata);
+ gtk_table_attach (GTK_TABLE (table), color_picker, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ label = gtk_label_new (_("Memory"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 1, 7, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table),
+ load_graph_get_labels(mem_graph)->memory,
+ 1,
+ 2,
+ 1,
+ 2,
+ GTK_FILL,
+ GTK_FILL,
+ 0,
+ 0);
+
+ table = gtk_table_new (2, 7, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (mem_legend_box), table,
+ TRUE, TRUE, 0);
+
+ 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), procdata);
+ gtk_table_attach (GTK_TABLE (table), color_picker, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ label = gtk_label_new (_("Swap"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 1, 7, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table),
+ load_graph_get_labels(mem_graph)->swap,
+ 1,
+ 2,
+ 1,
+ 2,
+ GTK_FILL,
+ GTK_FILL,
+ 0,
+ 0);
+
+ procdata->mem_graph = mem_graph;
+
+ /* The net box */
+ net_box = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), net_box, TRUE, TRUE, 0);
+
+ label = make_title_label (_("Network History"));
+ gtk_box_pack_start (GTK_BOX (net_box), label, FALSE, FALSE, 0);
+
+ net_graph_box = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (net_box), net_graph_box, TRUE, TRUE, 0);
+
+ net_graph = new LoadGraph(LOAD_GRAPH_NET);
+ gtk_box_pack_start (GTK_BOX (net_graph_box),
+ load_graph_get_widget(net_graph),
+ TRUE,
+ TRUE,
+ 0);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ spacer = gtk_label_new ("");
+ gtk_widget_set_size_request(GTK_WIDGET(spacer), 54, -1);
+ gtk_box_pack_start (GTK_BOX (hbox), spacer,
+ FALSE, FALSE, 0);
+
+
+ gtk_box_pack_start (GTK_BOX (net_graph_box), hbox,
+ FALSE, FALSE, 0);
+
+ net_legend_box = gtk_hbox_new(TRUE, 10);
+ gtk_box_pack_start (GTK_BOX (hbox), net_legend_box,
+ TRUE, TRUE, 0);
+
+ table = gtk_table_new (2, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (net_legend_box), table,
+ TRUE, TRUE, 0);
+
+ color_picker = gsm_color_button_new (
+ &net_graph->colors.at(0), GSMCP_TYPE_NETWORK_IN);
+ g_signal_connect (G_OBJECT (color_picker), "color_set",
+ G_CALLBACK (cb_net_in_color_changed), procdata);
+ gtk_table_attach (GTK_TABLE (table), color_picker, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ label = gtk_label_new (_("Receiving"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_misc_set_alignment (GTK_MISC (load_graph_get_labels(net_graph)->net_in),
+ 1.0,
+ 0.5);
+/*
+ hbox = gtk_hbox_new (FALSE, 0);
+ g_signal_connect (G_OBJECT (hbox), "size_request",
+ G_CALLBACK(size_request), &net_size);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ load_graph_get_labels(net_graph)->net_in,
+ TRUE,
+ TRUE,
+ 0);
+*/
+ gtk_widget_set_size_request(GTK_WIDGET(load_graph_get_labels(net_graph)->net_in), 65, -1);
+ gtk_table_attach (GTK_TABLE (table), load_graph_get_labels(net_graph)->net_in, 2, 3, 0, 1,
+ static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL), GTK_FILL, 0, 0);
+
+ label = gtk_label_new (_("Total Received"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_misc_set_alignment (GTK_MISC (load_graph_get_labels(net_graph)->net_in_total),
+ 1.0,
+ 0.5);
+ gtk_table_attach (GTK_TABLE (table),
+ load_graph_get_labels(net_graph)->net_in_total,
+ 2,
+ 3,
+ 1,
+ 2,
+ GTK_FILL,
+ GTK_FILL,
+ 0,
+ 0);
+
+ spacer = gtk_label_new ("");
+ gtk_widget_set_size_request(GTK_WIDGET(spacer), 38, -1);
+ gtk_table_attach (GTK_TABLE (table), spacer, 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (net_legend_box), table,
+ TRUE, TRUE, 0);
+
+ color_picker = gsm_color_button_new (
+ &net_graph->colors.at(1), GSMCP_TYPE_NETWORK_OUT);
+ g_signal_connect (G_OBJECT (color_picker), "color_set",
+ G_CALLBACK (cb_net_out_color_changed), procdata);
+ gtk_table_attach (GTK_TABLE (table), color_picker, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ label = gtk_label_new (_("Sending"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_misc_set_alignment (GTK_MISC (load_graph_get_labels(net_graph)->net_out),
+ 1.0,
+ 0.5);
+/*
+ hbox = gtk_hbox_new (FALSE, 0);
+ g_signal_connect (G_OBJECT (load_graph_get_labels(net_graph)->net_out), "size_request",
+ G_CALLBACK(size_request), &net_size);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ load_graph_get_labels(net_graph)->net_out,
+ TRUE,
+ TRUE,
+ 0);
+*/
+ gtk_widget_set_size_request(GTK_WIDGET(load_graph_get_labels(net_graph)->net_out), 65, -1);
+ gtk_table_attach (GTK_TABLE (table), load_graph_get_labels(net_graph)->net_out, 2, 3, 0, 1,
+ static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL), GTK_FILL, 0, 0);
+
+ label = gtk_label_new (_("Total Sent"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_misc_set_alignment (GTK_MISC (load_graph_get_labels(net_graph)->net_out_total),
+ 1.0,
+ 0.5);
+ gtk_table_attach (GTK_TABLE (table),
+ load_graph_get_labels(net_graph)->net_out_total,
+ 2,
+ 3,
+ 1,
+ 2,
+ GTK_FILL,
+ GTK_FILL,
+ 0,
+ 0);
+
+ spacer = gtk_label_new ("");
+ gtk_widget_set_size_request(GTK_WIDGET(spacer), 38, -1);
+ gtk_table_attach (GTK_TABLE (table), spacer, 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ procdata->net_graph = net_graph;
+
+ return vbox;
+}
+
+static void
+menu_item_select_cb (GtkMenuItem *proxy,
+ ProcData *procdata)
+{
+ GtkAction *action;
+ char *message;
+
+ action = gtk_activatable_get_related_action (GTK_ACTIVATABLE(proxy));
+ g_assert(action);
+
+ g_object_get (G_OBJECT (action), "tooltip", &message, NULL);
+ if (message)
+ {
+ gtk_statusbar_push (GTK_STATUSBAR (procdata->statusbar),
+ procdata->tip_message_cid, message);
+ g_free (message);
+ }
+}
+
+static void
+menu_item_deselect_cb (GtkMenuItem *proxy,
+ ProcData *procdata)
+{
+ gtk_statusbar_pop (GTK_STATUSBAR (procdata->statusbar),
+ procdata->tip_message_cid);
+}
+
+static void
+connect_proxy_cb (GtkUIManager *manager,
+ GtkAction *action,
+ GtkWidget *proxy,
+ ProcData *procdata)
+{
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ g_signal_connect (proxy, "select",
+ G_CALLBACK (menu_item_select_cb), procdata);
+ g_signal_connect (proxy, "deselect",
+ G_CALLBACK (menu_item_deselect_cb), procdata);
+ }
+}
+
+static void
+disconnect_proxy_cb (GtkUIManager *manager,
+ GtkAction *action,
+ GtkWidget *proxy,
+ ProcData *procdata)
+{
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ g_signal_handlers_disconnect_by_func
+ (proxy, (void*)(G_CALLBACK(menu_item_select_cb)), procdata);
+ g_signal_handlers_disconnect_by_func
+ (proxy, (void*)(G_CALLBACK(menu_item_deselect_cb)), procdata);
+ }
+}
+
+void
+create_main_window (ProcData *procdata)
+{
+ gint width, height;
+ GtkWidget *app;
+ GtkAction *action;
+ GtkWidget *menubar;
+ GtkWidget *main_box;
+ GtkWidget *notebook;
+ GtkWidget *tab_label1, *tab_label2, *tab_label3;
+ GtkWidget *vbox1;
+ GtkWidget *sys_box, *devices_box;
+ GtkWidget *sysinfo_box, *sysinfo_label;
+
+ app = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(app), _("System Monitor"));
+
+ GdkScreen* screen = gtk_widget_get_screen(app);
+ GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen);
+
+ /* use rgba colormap, if available */
+ if (colormap)
+ gtk_widget_set_default_colormap(colormap);
+
+ main_box = gtk_vbox_new (FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(app), main_box);
+
+ width = procdata->config.width;
+ height = procdata->config.height;
+ gtk_window_set_default_size (GTK_WINDOW (app), width, height);
+ gtk_window_set_resizable (GTK_WINDOW (app), TRUE);
+
+ /* create the menubar */
+ procdata->uimanager = gtk_ui_manager_new ();
+
+ /* show tooltips in the statusbar */
+ g_signal_connect (procdata->uimanager, "connect_proxy",
+ G_CALLBACK (connect_proxy_cb), procdata);
+ g_signal_connect (procdata->uimanager, "disconnect_proxy",
+ G_CALLBACK (disconnect_proxy_cb), procdata);
+
+ gtk_window_add_accel_group (GTK_WINDOW (app),
+ gtk_ui_manager_get_accel_group (procdata->uimanager));
+
+ if (!gtk_ui_manager_add_ui_from_string (procdata->uimanager,
+ ui_info,
+ -1,
+ NULL)) {
+ g_error("building menus failed");
+ }
+
+ procdata->action_group = gtk_action_group_new ("ProcmanActions");
+ gtk_action_group_set_translation_domain (procdata->action_group, NULL);
+ gtk_action_group_add_actions (procdata->action_group,
+ menu_entries,
+ G_N_ELEMENTS (menu_entries),
+ procdata);
+ gtk_action_group_add_toggle_actions (procdata->action_group,
+ toggle_menu_entries,
+ G_N_ELEMENTS (toggle_menu_entries),
+ procdata);
+
+ gtk_action_group_add_radio_actions (procdata->action_group,
+ radio_menu_entries,
+ G_N_ELEMENTS (radio_menu_entries),
+ procdata->config.whose_process,
+ G_CALLBACK(cb_radio_processes),
+ procdata);
+
+ gtk_ui_manager_insert_action_group (procdata->uimanager,
+ procdata->action_group,
+ 0);
+
+ menubar = gtk_ui_manager_get_widget (procdata->uimanager, "/MenuBar");
+ gtk_box_pack_start (GTK_BOX (main_box), menubar, FALSE, FALSE, 0);
+
+
+ /* create the main notebook */
+ procdata->notebook = notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (main_box),
+ notebook,
+ TRUE,
+ TRUE,
+ 0);
+
+ sysinfo_box = gtk_hbox_new(TRUE, 0); // procman_create_sysinfo_view();
+ sysinfo_label = gtk_label_new(_("System"));
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sysinfo_box, sysinfo_label);
+
+ vbox1 = create_proc_view (procdata);
+ tab_label1 = gtk_label_new (_("Processes"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox1, tab_label1);
+
+ sys_box = create_sys_view (procdata);
+ tab_label2 = gtk_label_new (_("Resources"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sys_box, tab_label2);
+
+ devices_box = create_disk_view (procdata);
+ tab_label3 = gtk_label_new (_("File Systems"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), devices_box, tab_label3);
+
+ g_signal_connect (G_OBJECT (notebook), "switch-page",
+ G_CALLBACK (cb_switch_page), procdata);
+ g_signal_connect (G_OBJECT (notebook), "change-current-page",
+ G_CALLBACK (cb_change_current_page), procdata);
+
+ gtk_widget_show_all(notebook); // need to make page switch work
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), procdata->config.current_tab);
+ cb_change_current_page (GTK_NOTEBOOK (notebook), procdata->config.current_tab, procdata);
+ g_signal_connect (G_OBJECT (app), "delete_event",
+ G_CALLBACK (cb_app_delete),
+ procdata);
+
+
+ /* create the statusbar */
+ procdata->statusbar = gtk_statusbar_new();
+ gtk_box_pack_end(GTK_BOX(main_box), procdata->statusbar, FALSE, FALSE, 0);
+ procdata->tip_message_cid = gtk_statusbar_get_context_id
+ (GTK_STATUSBAR (procdata->statusbar), "tip_message");
+
+
+ action = gtk_action_group_get_action (procdata->action_group, "ShowDependencies");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ procdata->config.show_tree);
+
+ gtk_widget_show_all(app);
+ procdata->app = app;
+}
+
+void
+do_popup_menu (ProcData *procdata, GdkEventButton *event)
+{
+ guint button;
+ guint32 event_time;
+
+ if (event) {
+ button = event->button;
+ event_time = event->time;
+ }
+ else {
+ button = 0;
+ event_time = gtk_get_current_event_time ();
+ }
+
+ gtk_menu_popup (GTK_MENU (procdata->popup_menu), NULL, NULL,
+ NULL, NULL, button, event_time);
+}
+
+void
+update_sensitivity(ProcData *data)
+{
+ const char * const selected_actions[] = { "StopProcess",
+ "ContProcess",
+ "EndProcess",
+ "KillProcess",
+ "ChangePriority",
+ "MemoryMaps",
+ "OpenFiles" };
+
+ const char * const processes_actions[] = { "ShowActiveProcesses",
+ "ShowAllProcesses",
+ "ShowMyProcesses",
+ "ShowDependencies",
+ "Refresh"
+ };
+
+ size_t i;
+ gboolean processes_sensitivity, selected_sensitivity;
+ GtkAction *action;
+
+ processes_sensitivity = (data->config.current_tab == PROCMAN_TAB_PROCESSES);
+ selected_sensitivity = (processes_sensitivity && data->selected_process != NULL);
+
+ if(data->endprocessbutton) {
+ /* avoid error on startup if endprocessbutton
+ has not been built yet */
+ gtk_widget_set_sensitive(data->endprocessbutton, selected_sensitivity);
+ }
+
+ for (i = 0; i != G_N_ELEMENTS(processes_actions); ++i) {
+ action = gtk_action_group_get_action(data->action_group,
+ processes_actions[i]);
+ gtk_action_set_sensitive(action, processes_sensitivity);
+ }
+
+ for (i = 0; i != G_N_ELEMENTS(selected_actions); ++i) {
+ action = gtk_action_group_get_action(data->action_group,
+ selected_actions[i]);
+ gtk_action_set_sensitive(action, selected_sensitivity);
+ }
+}
+
+static void
+cb_toggle_tree (GtkAction *action, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfClient *client = procdata->client;
+ gboolean show;
+
+ show = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+ if (show == procdata->config.show_tree)
+ return;
+
+ mateconf_client_set_bool (client, "/apps/procman/show_tree", show, NULL);
+}
diff --git a/src/interface.h b/src/interface.h
new file mode 100644
index 0000000..4bda35a
--- /dev/null
+++ b/src/interface.h
@@ -0,0 +1,32 @@
+/* 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _PROCMAN_INTERFACE_H_
+#define _PROCMAN_INTERFACE_H_
+
+#include <glib/gtypes.h>
+#include <gtk/gtk.h>
+#include "procman.h"
+
+void create_main_window (ProcData *data);
+void update_sensitivity (ProcData *data);
+void do_popup_menu(ProcData *data, GdkEventButton *event);
+GtkWidget * make_title_label (const char *text);
+
+#endif /* _PROCMAN_INTERFACE_H_ */
diff --git a/src/load-graph.cpp b/src/load-graph.cpp
new file mode 100644
index 0000000..79700d4
--- /dev/null
+++ b/src/load-graph.cpp
@@ -0,0 +1,787 @@
+#include <config.h>
+
+#include <gdkmm/pixbuf.h>
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <signal.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <gdk/gdkx.h>
+
+#include <glib/gi18n.h>
+
+#include <glibtop.h>
+#include <glibtop/cpu.h>
+#include <glibtop/mem.h>
+#include <glibtop/swap.h>
+#include <glibtop/netload.h>
+#include <glibtop/netlist.h>
+#include <math.h>
+
+#include <algorithm>
+
+#include "procman.h"
+#include "load-graph.h"
+#include "util.h"
+#include "gsm_color_button.h"
+
+
+void LoadGraph::clear_background()
+{
+ if (this->background) {
+ g_object_unref(this->background);
+ this->background = NULL;
+ }
+}
+
+
+unsigned LoadGraph::num_bars() const
+{
+ unsigned n;
+
+ // keep 100 % num_bars == 0
+ switch (static_cast<int>(this->draw_height / (this->fontsize + 14)))
+ {
+ case 0:
+ case 1:
+ n = 1;
+ break;
+ case 2:
+ case 3:
+ n = 2;
+ break;
+ case 4:
+ n = 4;
+ break;
+ default:
+ n = 5;
+ }
+
+ return n;
+}
+
+
+
+#define FRAME_WIDTH 4
+void draw_background(LoadGraph *g) {
+ GtkAllocation allocation;
+ double dash[2] = { 1.0, 2.0 };
+ cairo_t *cr;
+ guint i;
+ unsigned num_bars;
+ char *caption;
+ cairo_text_extents_t extents;
+
+ num_bars = g->num_bars();
+ g->graph_dely = (g->draw_height - 15) / num_bars; /* round to int to avoid AA blur */
+ g->real_draw_height = g->graph_dely * num_bars;
+ g->graph_delx = (g->draw_width - 2.0 - g->rmargin - g->indent) / (LoadGraph::NUM_POINTS - 3);
+ g->graph_buffer_offset = (int) (1.5 * g->graph_delx) + FRAME_WIDTH ;
+
+ gtk_widget_get_allocation (g->disp, &allocation);
+ g->background = gdk_pixmap_new (GDK_DRAWABLE (gtk_widget_get_window (g->disp)),
+ allocation.width,
+ allocation.height,
+ -1);
+ cr = gdk_cairo_create (g->background);
+
+ // set the background colour
+ GtkStyle *style = gtk_widget_get_style (ProcData::get_instance()->notebook);
+ gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
+ cairo_paint (cr);
+
+ /* draw frame */
+ cairo_translate (cr, FRAME_WIDTH, FRAME_WIDTH);
+
+ /* Draw background rectangle */
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_rectangle (cr, g->rmargin + g->indent, 0,
+ g->draw_width - g->rmargin - g->indent, g->real_draw_height);
+ cairo_fill(cr);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_dash (cr, dash, 2, 0);
+ cairo_set_font_size (cr, g->fontsize);
+
+ for (i = 0; i <= num_bars; ++i) {
+ double y;
+
+ if (i == 0)
+ y = 0.5 + g->fontsize / 2.0;
+ else if (i == num_bars)
+ y = i * g->graph_dely + 0.5;
+ else
+ y = i * g->graph_dely + g->fontsize / 2.0;
+
+ gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]);
+ if (g->type == LOAD_GRAPH_NET) {
+ // operation orders matters so it's 0 if i == num_bars
+ unsigned rate = g->net.max - (i * g->net.max / num_bars);
+ const std::string caption(procman::format_network_rate(rate, g->net.max));
+ cairo_text_extents (cr, caption.c_str(), &extents);
+ cairo_move_to (cr, g->indent - extents.width + 20, y);
+ cairo_show_text (cr, caption.c_str());
+ } else {
+ // operation orders matters so it's 0 if i == num_bars
+ caption = g_strdup_printf("%d %%", 100 - i * (100 / num_bars));
+ cairo_text_extents (cr, caption, &extents);
+ cairo_move_to (cr, g->indent - extents.width + 20, y);
+ cairo_show_text (cr, caption);
+ g_free (caption);
+ }
+
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.75);
+ cairo_move_to (cr, g->rmargin + g->indent - 3, i * g->graph_dely + 0.5);
+ cairo_line_to (cr, g->draw_width - 0.5, i * g->graph_dely + 0.5);
+ }
+ cairo_stroke (cr);
+
+ cairo_set_dash (cr, dash, 2, 1.5);
+
+ const unsigned total_seconds = g->speed * (LoadGraph::NUM_POINTS - 2) / 1000;
+
+ for (unsigned int i = 0; i < 7; i++) {
+ double x = (i) * (g->draw_width - g->rmargin - g->indent) / 6;
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.75);
+ cairo_move_to (cr, (ceil(x) + 0.5) + g->rmargin + g->indent, 0.5);
+ cairo_line_to (cr, (ceil(x) + 0.5) + g->rmargin + g->indent, g->real_draw_height + 4.5);
+ cairo_stroke(cr);
+ unsigned seconds = total_seconds - i * total_seconds / 6;
+ const char* format;
+ if (i == 0)
+ format = dngettext(GETTEXT_PACKAGE, "%u second", "%u seconds", seconds);
+ else
+ format = "%u";
+ caption = g_strdup_printf(format, seconds);
+ cairo_text_extents (cr, caption, &extents);
+ cairo_move_to (cr, ((ceil(x) + 0.5) + g->rmargin + g->indent) - (extents.width/2), g->draw_height);
+ gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]);
+ cairo_show_text (cr, caption);
+ g_free (caption);
+ }
+
+ cairo_stroke (cr);
+ cairo_destroy (cr);
+}
+
+/* Redraws the backing buffer for the load graph and updates the window */
+void
+load_graph_draw (LoadGraph *g)
+{
+ /* repaint */
+ gtk_widget_queue_draw (g->disp);
+}
+
+static int load_graph_update (gpointer user_data); // predeclare load_graph_update so we can compile ;)
+
+static gboolean
+load_graph_configure (GtkWidget *widget,
+ GdkEventConfigure *event,
+ gpointer data_ptr)
+{
+ GtkAllocation allocation;
+ LoadGraph * const g = static_cast<LoadGraph*>(data_ptr);
+
+ gtk_widget_get_allocation (widget, &allocation);
+ g->draw_width = allocation.width - 2 * FRAME_WIDTH;
+ g->draw_height = allocation.height - 2 * FRAME_WIDTH;
+
+ g->clear_background();
+
+ if (g->gc == NULL) {
+ g->gc = gdk_gc_new (GDK_DRAWABLE (gtk_widget_get_window (widget)));
+ }
+
+ load_graph_draw (g);
+
+ return TRUE;
+}
+
+static gboolean
+load_graph_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer data_ptr)
+{
+ LoadGraph * const g = static_cast<LoadGraph*>(data_ptr);
+ GtkAllocation allocation;
+ GdkWindow *window;
+
+ guint i, j;
+ gdouble sample_width, x_offset;
+
+ if (g->background == NULL) {
+ draw_background(g);
+ }
+
+ window = gtk_widget_get_window (g->disp);
+ gtk_widget_get_allocation (g->disp, &allocation);
+ gdk_draw_drawable (window,
+ g->gc,
+ g->background,
+ 0, 0, 0, 0,
+ allocation.width,
+ allocation.height);
+
+ /* Number of pixels wide for one graph point */
+ sample_width = (float)(g->draw_width - g->rmargin - g->indent) / (float)LoadGraph::NUM_POINTS;
+ /* General offset */
+ x_offset = g->draw_width - g->rmargin + (sample_width*2);
+
+ /* Subframe offset */
+ x_offset += g->rmargin - ((sample_width / g->frames_per_unit) * g->render_counter);
+
+ /* draw the graph */
+ cairo_t* cr;
+
+ cr = gdk_cairo_create (window);
+
+ cairo_set_line_width (cr, 1);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+ cairo_rectangle (cr, g->rmargin + g->indent + FRAME_WIDTH + 1, FRAME_WIDTH - 1,
+ g->draw_width - g->rmargin - g->indent - 1, g->real_draw_height + FRAME_WIDTH - 1);
+ cairo_clip(cr);
+
+ for (j = 0; j < g->n; ++j) {
+ cairo_move_to (cr, x_offset, (1.0f - g->data[0][j]) * g->real_draw_height);
+ gdk_cairo_set_source_color (cr, &(g->colors [j]));
+
+ for (i = 1; i < LoadGraph::NUM_POINTS; ++i) {
+ if (g->data[i][j] == -1.0f)
+ continue;
+ cairo_curve_to (cr,
+ x_offset - ((i - 0.5f) * g->graph_delx),
+ (1.0f - g->data[i-1][j]) * g->real_draw_height + 3.5f,
+ x_offset - ((i - 0.5f) * g->graph_delx),
+ (1.0f - g->data[i][j]) * g->real_draw_height + 3.5f,
+ x_offset - (i * g->graph_delx),
+ (1.0f - g->data[i][j]) * g->real_draw_height + 3.5f);
+ }
+ cairo_stroke (cr);
+
+ }
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static void
+get_load (LoadGraph *g)
+{
+ guint i;
+ glibtop_cpu cpu;
+
+ glibtop_get_cpu (&cpu);
+
+#undef NOW
+#undef LAST
+#define NOW (g->cpu.times[g->cpu.now])
+#define LAST (g->cpu.times[g->cpu.now ^ 1])
+
+ if (g->n == 1) {
+ NOW[0][CPU_TOTAL] = cpu.total;
+ NOW[0][CPU_USED] = cpu.user + cpu.nice + cpu.sys;
+ } else {
+ for (i = 0; i < g->n; i++) {
+ NOW[i][CPU_TOTAL] = cpu.xcpu_total[i];
+ NOW[i][CPU_USED] = cpu.xcpu_user[i] + cpu.xcpu_nice[i]
+ + cpu.xcpu_sys[i];
+ }
+ }
+
+ // on the first call, LAST is 0
+ // which means data is set to the average load since boot
+ // that value has no meaning, we just want all the
+ // graphs to be aligned, so the CPU graph needs to start
+ // immediately
+
+ for (i = 0; i < g->n; i++) {
+ float load;
+ float total, used;
+ gchar *text;
+
+ total = NOW[i][CPU_TOTAL] - LAST[i][CPU_TOTAL];
+ used = NOW[i][CPU_USED] - LAST[i][CPU_USED];
+
+ load = used / MAX(total, 1.0f);
+ g->data[0][i] = load;
+
+ /* Update label */
+ text = g_strdup_printf("%.1f%%", load * 100.0f);
+ gtk_label_set_text(GTK_LABEL(g->labels.cpu[i]), text);
+ g_free(text);
+ }
+
+ g->cpu.now ^= 1;
+
+#undef NOW
+#undef LAST
+}
+
+
+namespace
+{
+
+ void set_memory_label_and_picker(GtkLabel* label, GSMColorButton* picker,
+ guint64 used, guint64 total, double percent)
+ {
+ char* used_text;
+ char* total_text;
+ char* text;
+
+ used_text = procman::format_size(used);
+ total_text = procman::format_size(total);
+ // xgettext: 540MiB (53 %) of 1.0 GiB
+ text = g_strdup_printf(_("%s (%.1f %%) of %s"), used_text, 100.0 * percent, total_text);
+ gtk_label_set_text(label, text);
+ g_free(used_text);
+ g_free(total_text);
+ g_free(text);
+
+ if (picker)
+ gsm_color_button_set_fraction(picker, percent);
+ }
+}
+
+static void
+get_memory (LoadGraph *g)
+{
+ float mempercent, swappercent;
+
+ glibtop_mem mem;
+ glibtop_swap swap;
+
+ glibtop_get_mem (&mem);
+ glibtop_get_swap (&swap);
+
+ /* There's no swap on LiveCD : 0.0f is better than NaN :) */
+ swappercent = (swap.total ? (float)swap.used / (float)swap.total : 0.0f);
+ mempercent = (float)mem.user / (float)mem.total;
+
+ set_memory_label_and_picker(GTK_LABEL(g->labels.memory),
+ GSM_COLOR_BUTTON(g->mem_color_picker),
+ mem.user, mem.total, mempercent);
+
+ set_memory_label_and_picker(GTK_LABEL(g->labels.swap),
+ GSM_COLOR_BUTTON(g->swap_color_picker),
+ swap.used, swap.total, swappercent);
+
+ g->data[0][0] = mempercent;
+ g->data[0][1] = swappercent;
+}
+
+static void
+net_scale (LoadGraph *g, unsigned din, unsigned dout)
+{
+ g->data[0][0] = 1.0f * din / g->net.max;
+ g->data[0][1] = 1.0f * dout / g->net.max;
+
+ unsigned dmax = std::max(din, dout);
+ g->net.values[g->net.cur] = dmax;
+ g->net.cur = (g->net.cur + 1) % LoadGraph::NUM_POINTS;
+
+ unsigned new_max;
+ // both way, new_max is the greatest value
+ if (dmax >= g->net.max)
+ new_max = dmax;
+ else
+ new_max = *std::max_element(&g->net.values[0],
+ &g->net.values[LoadGraph::NUM_POINTS]);
+
+ //
+ // Round network maximum
+ //
+
+ const unsigned bak_max(new_max);
+
+ if (ProcData::get_instance()->config.network_in_bits) {
+ // TODO: fix logic to give a nice scale with bits
+
+ // round up to get some extra space
+ // yes, it can overflow
+ new_max = 1.1 * new_max;
+ // make sure max is not 0 to avoid / 0
+ // default to 125 bytes == 1kbit
+ new_max = std::max(new_max, 125U);
+
+ } else {
+ // round up to get some extra space
+ // yes, it can overflow
+ new_max = 1.1 * new_max;
+ // make sure max is not 0 to avoid / 0
+ // default to 1 KiB
+ new_max = std::max(new_max, 1024U);
+
+ // decompose new_max = coef10 * 2**(base10 * 10)
+ // where coef10 and base10 are integers and coef10 < 2**10
+ //
+ // e.g: ceil(100.5 KiB) = 101 KiB = 101 * 2**(1 * 10)
+ // where base10 = 1, coef10 = 101, pow2 = 16
+
+ unsigned pow2 = std::floor(log2(new_max));
+ unsigned base10 = pow2 / 10;
+ unsigned coef10 = std::ceil(new_max / double(1UL << (base10 * 10)));
+ g_assert(new_max <= (coef10 * (1UL << (base10 * 10))));
+
+ // then decompose coef10 = x * 10**factor10
+ // where factor10 is integer and x < 10
+ // so we new_max has only 1 significant digit
+
+ unsigned factor10 = std::pow(10.0, std::floor(std::log10(coef10)));
+ coef10 = std::ceil(coef10 / double(factor10)) * factor10;
+
+ // then make coef10 divisible by num_bars
+ if (coef10 % g->num_bars() != 0)
+ coef10 = coef10 + (g->num_bars() - coef10 % g->num_bars());
+ g_assert(coef10 % g->num_bars() == 0);
+
+ new_max = coef10 * (1UL << (base10 * 10));
+ procman_debug("bak %u new_max %u pow2 %u coef10 %u", bak_max, new_max, pow2, coef10);
+ }
+
+ if (bak_max > new_max) {
+ procman_debug("overflow detected: bak=%u new=%u", bak_max, new_max);
+ new_max = bak_max;
+ }
+
+ // if max is the same or has decreased but not so much, don't
+ // do anything to avoid rescaling
+ if ((0.8 * g->net.max) < new_max && new_max <= g->net.max)
+ return;
+
+ const float scale = 1.0f * g->net.max / new_max;
+
+ for (size_t i = 0; i < LoadGraph::NUM_POINTS; i++) {
+ if (g->data[i][0] >= 0.0f) {
+ g->data[i][0] *= scale;
+ g->data[i][1] *= scale;
+ }
+ }
+
+ procman_debug("rescale dmax = %u max = %u new_max = %u", dmax, g->net.max, new_max);
+
+ g->net.max = new_max;
+
+ // force the graph background to be redrawn now that scale has changed
+ g->clear_background();
+}
+
+static void
+get_net (LoadGraph *g)
+{
+ glibtop_netlist netlist;
+ char **ifnames;
+ guint32 i;
+ guint64 in = 0, out = 0;
+ GTimeVal time;
+ unsigned din, dout;
+
+ ifnames = glibtop_get_netlist(&netlist);
+
+ for (i = 0; i < netlist.number; ++i)
+ {
+ glibtop_netload netload;
+ glibtop_get_netload (&netload, ifnames[i]);
+
+ if (netload.if_flags & (1 << GLIBTOP_IF_FLAGS_LOOPBACK))
+ continue;
+
+ /* Skip interfaces without any IPv4/IPv6 address (or
+ those with only a LINK ipv6 addr) However we need to
+ be able to exclude these while still keeping the
+ value so when they get online (with NetworkManager
+ for example) we don't get a suddent peak. Once we're
+ able to get this, ignoring down interfaces will be
+ possible too. */
+ if (not (netload.flags & (1 << GLIBTOP_NETLOAD_ADDRESS6)
+ and netload.scope6 != GLIBTOP_IF_IN6_SCOPE_LINK)
+ and not (netload.flags & (1 << GLIBTOP_NETLOAD_ADDRESS)))
+ continue;
+
+ /* Don't skip interfaces that are down (GLIBTOP_IF_FLAGS_UP)
+ to avoid spikes when they are brought up */
+
+ in += netload.bytes_in;
+ out += netload.bytes_out;
+ }
+
+ g_strfreev(ifnames);
+
+ g_get_current_time (&time);
+
+ if (in >= g->net.last_in && out >= g->net.last_out &&
+ g->net.time.tv_sec != 0) {
+ float dtime;
+ dtime = time.tv_sec - g->net.time.tv_sec +
+ (float) (time.tv_usec - g->net.time.tv_usec) / G_USEC_PER_SEC;
+ din = static_cast<unsigned>((in - g->net.last_in) / dtime);
+ dout = static_cast<unsigned>((out - g->net.last_out) / dtime);
+ } else {
+ /* Don't calc anything if new data is less than old (interface
+ removed, counters reset, ...) or if it is the first time */
+ din = 0;
+ dout = 0;
+ }
+
+ g->net.last_in = in;
+ g->net.last_out = out;
+ g->net.time = time;
+
+ net_scale(g, din, dout);
+
+
+ gtk_label_set_text (GTK_LABEL (g->labels.net_in), procman::format_network_rate(din).c_str());
+ gtk_label_set_text (GTK_LABEL (g->labels.net_in_total), procman::format_network(in).c_str());
+
+ gtk_label_set_text (GTK_LABEL (g->labels.net_out), procman::format_network_rate(dout).c_str());
+ gtk_label_set_text (GTK_LABEL (g->labels.net_out_total), procman::format_network(out).c_str());
+}
+
+
+/* Updates the load graph when the timeout expires */
+static gboolean
+load_graph_update (gpointer user_data)
+{
+ LoadGraph * const g = static_cast<LoadGraph*>(user_data);
+
+ if (g->render_counter == g->frames_per_unit - 1) {
+ std::rotate(&g->data[0], &g->data[LoadGraph::NUM_POINTS - 1], &g->data[LoadGraph::NUM_POINTS]);
+
+ switch (g->type) {
+ case LOAD_GRAPH_CPU:
+ get_load(g);
+ break;
+ case LOAD_GRAPH_MEM:
+ get_memory(g);
+ break;
+ case LOAD_GRAPH_NET:
+ get_net(g);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ if (g->draw)
+ load_graph_draw (g);
+
+ g->render_counter++;
+
+ if (g->render_counter >= g->frames_per_unit)
+ g->render_counter = 0;
+
+ return TRUE;
+}
+
+
+
+LoadGraph::~LoadGraph()
+{
+ load_graph_stop(this);
+
+ if (this->timer_index)
+ g_source_remove(this->timer_index);
+
+ this->clear_background();
+}
+
+
+
+static gboolean
+load_graph_destroy (GtkWidget *widget, gpointer data_ptr)
+{
+ LoadGraph * const g = static_cast<LoadGraph*>(data_ptr);
+
+ delete g;
+
+ return FALSE;
+}
+
+
+LoadGraph::LoadGraph(guint type)
+ : fontsize(0.0),
+ rmargin(0.0),
+ indent(0.0),
+ n(0),
+ type(0),
+ speed(0),
+ draw_width(0),
+ draw_height(0),
+ render_counter(0),
+ frames_per_unit(0),
+ graph_dely(0),
+ real_draw_height(0),
+ graph_delx(0.0),
+ graph_buffer_offset(0),
+ main_widget(NULL),
+ disp(NULL),
+ gc(NULL),
+ background(NULL),
+ timer_index(0),
+ draw(FALSE),
+ mem_color_picker(NULL),
+ swap_color_picker(NULL)
+{
+ LoadGraph * const g = this;
+
+ // FIXME:
+ // on configure, g->frames_per_unit = g->draw_width/(LoadGraph::NUM_POINTS);
+ // knock FRAMES down to 5 until cairo gets faster
+ g->frames_per_unit = 10; // this will be changed but needs initialising
+ g->fontsize = 8.0;
+ g->rmargin = 3.5 * g->fontsize;
+ g->indent = 24.0;
+
+ g->type = type;
+ switch (type) {
+ case LOAD_GRAPH_CPU:
+ memset(&this->cpu, 0, sizeof g->cpu);
+ g->n = ProcData::get_instance()->config.num_cpus;
+
+ for(guint i = 0; i < G_N_ELEMENTS(g->labels.cpu); ++i)
+ g->labels.cpu[i] = gtk_label_new(NULL);
+
+ break;
+
+ case LOAD_GRAPH_MEM:
+ g->n = 2;
+ g->labels.memory = gtk_label_new(NULL);
+ g->labels.swap = gtk_label_new(NULL);
+ break;
+
+ case LOAD_GRAPH_NET:
+ memset(&this->net, 0, sizeof g->net);
+ g->n = 2;
+ g->net.max = 1;
+ g->labels.net_in = gtk_label_new(NULL);
+ g->labels.net_in_total = gtk_label_new(NULL);
+ g->labels.net_out = gtk_label_new(NULL);
+ g->labels.net_out_total = gtk_label_new(NULL);
+ break;
+ }
+
+ g->speed = ProcData::get_instance()->config.graph_update_interval;
+
+ g->colors.resize(g->n);
+
+ switch (type) {
+ case LOAD_GRAPH_CPU:
+ memcpy(&g->colors[0], ProcData::get_instance()->config.cpu_color,
+ g->n * sizeof g->colors[0]);
+ break;
+ case LOAD_GRAPH_MEM:
+ g->colors[0] = ProcData::get_instance()->config.mem_color;
+ g->colors[1] = ProcData::get_instance()->config.swap_color;
+ g->mem_color_picker = gsm_color_button_new (&g->colors[0],
+ GSMCP_TYPE_PIE);
+ g->swap_color_picker = gsm_color_button_new (&g->colors[1],
+ GSMCP_TYPE_PIE);
+ break;
+ case LOAD_GRAPH_NET:
+ g->colors[0] = ProcData::get_instance()->config.net_in_color;
+ g->colors[1] = ProcData::get_instance()->config.net_out_color;
+ break;
+ }
+
+ g->timer_index = 0;
+ g->render_counter = (g->frames_per_unit - 1);
+ g->draw = FALSE;
+
+ g->main_widget = gtk_vbox_new (FALSE, FALSE);
+ gtk_widget_set_size_request(g->main_widget, -1, LoadGraph::GRAPH_MIN_HEIGHT);
+ gtk_widget_show (g->main_widget);
+
+ g->disp = gtk_drawing_area_new ();
+ gtk_widget_show (g->disp);
+ g_signal_connect (G_OBJECT (g->disp), "expose_event",
+ G_CALLBACK (load_graph_expose), g);
+ g_signal_connect (G_OBJECT(g->disp), "configure_event",
+ G_CALLBACK (load_graph_configure), g);
+ g_signal_connect (G_OBJECT(g->disp), "destroy",
+ G_CALLBACK (load_graph_destroy), g);
+
+ gtk_widget_set_events (g->disp, GDK_EXPOSURE_MASK);
+
+ gtk_box_pack_start (GTK_BOX (g->main_widget), g->disp, TRUE, TRUE, 0);
+
+
+ /* Allocate data in a contiguous block */
+ g->data_block = std::vector<float>(g->n * LoadGraph::NUM_POINTS, -1.0f);
+
+ for (guint i = 0; i < LoadGraph::NUM_POINTS; ++i)
+ g->data[i] = &g->data_block[0] + i * g->n;
+
+ gtk_widget_show_all (g->main_widget);
+}
+
+void
+load_graph_start (LoadGraph *g)
+{
+ if(!g->timer_index) {
+
+ load_graph_update(g);
+
+ g->timer_index = g_timeout_add (g->speed / g->frames_per_unit,
+ load_graph_update,
+ g);
+ }
+
+ g->draw = TRUE;
+}
+
+void
+load_graph_stop (LoadGraph *g)
+{
+ /* don't draw anymore, but continue to poll */
+ g->draw = FALSE;
+}
+
+void
+load_graph_change_speed (LoadGraph *g,
+ guint new_speed)
+{
+ if (g->speed == new_speed)
+ return;
+
+ g->speed = new_speed;
+
+ g_assert(g->timer_index);
+
+ if(g->timer_index) {
+ g_source_remove (g->timer_index);
+ g->timer_index = g_timeout_add (g->speed / g->frames_per_unit,
+ load_graph_update,
+ g);
+ }
+
+ g->clear_background();
+}
+
+
+LoadGraphLabels*
+load_graph_get_labels (LoadGraph *g)
+{
+ return &g->labels;
+}
+
+GtkWidget*
+load_graph_get_widget (LoadGraph *g)
+{
+ return g->main_widget;
+}
+
+GtkWidget*
+load_graph_get_mem_color_picker(LoadGraph *g)
+{
+ return g->mem_color_picker;
+}
+
+GtkWidget*
+load_graph_get_swap_color_picker(LoadGraph *g)
+{
+ return g->swap_color_picker;
+}
diff --git a/src/load-graph.h b/src/load-graph.h
new file mode 100644
index 0000000..6111c78
--- /dev/null
+++ b/src/load-graph.h
@@ -0,0 +1,131 @@
+#ifndef _PROCMAN_LOAD_GRAPH_H_
+#define _PROCMAN_LOAD_GRAPH_H_
+
+#include <glib/gtypes.h>
+#include <glibtop/cpu.h>
+
+enum
+{
+ LOAD_GRAPH_CPU,
+ LOAD_GRAPH_MEM,
+ LOAD_GRAPH_NET
+};
+
+
+enum {
+ CPU_TOTAL,
+ CPU_USED,
+ N_CPU_STATES
+};
+
+
+struct LoadGraphLabels
+{
+ GtkWidget *cpu[GLIBTOP_NCPU];
+ GtkWidget *memory;
+ GtkWidget *swap;
+ GtkWidget *net_in;
+ GtkWidget *net_in_total;
+ GtkWidget *net_out;
+ GtkWidget *net_out_total;
+};
+
+
+
+struct LoadGraph {
+
+ static const unsigned NUM_POINTS = 60 + 2;
+ static const unsigned GRAPH_MIN_HEIGHT = 40;
+
+ LoadGraph(guint type);
+ ~LoadGraph();
+
+ unsigned num_bars() const;
+ void clear_background();
+
+ double fontsize;
+ double rmargin;
+ double indent;
+
+ guint n;
+ gint type;
+ guint speed;
+ guint draw_width, draw_height;
+ guint render_counter;
+ guint frames_per_unit;
+ guint graph_dely;
+ guint real_draw_height;
+ double graph_delx;
+ guint graph_buffer_offset;
+
+ std::vector<GdkColor> colors;
+
+ std::vector<float> data_block;
+ gfloat* data[NUM_POINTS];
+
+ GtkWidget *main_widget;
+ GtkWidget *disp;
+
+ GdkGC *gc;
+ GdkDrawable *background;
+
+ guint timer_index;
+
+ gboolean draw;
+
+ LoadGraphLabels labels;
+ GtkWidget *mem_color_picker;
+ GtkWidget *swap_color_picker;
+
+ /* union { */
+ struct {
+ guint now; /* 0 -> current, 1 -> last
+ now ^ 1 each time */
+ /* times[now], times[now ^ 1] is last */
+ guint64 times[2][GLIBTOP_NCPU][N_CPU_STATES];
+ } cpu;
+
+ struct {
+ guint64 last_in, last_out;
+ GTimeVal time;
+ unsigned int max;
+ unsigned values[NUM_POINTS];
+ size_t cur;
+ } net;
+ /* }; */
+};
+
+
+
+/* Force a drawing update */
+void
+load_graph_draw (LoadGraph *g);
+
+/* Start load graph. */
+void
+load_graph_start (LoadGraph *g);
+
+/* Stop load graph. */
+void
+load_graph_stop (LoadGraph *g);
+
+/* Change load graph speed and restart it if it has been previously started */
+void
+load_graph_change_speed (LoadGraph *g,
+ guint new_speed);
+
+LoadGraphLabels*
+load_graph_get_labels (LoadGraph *g) G_GNUC_CONST;
+
+
+GtkWidget*
+load_graph_get_widget (LoadGraph *g) G_GNUC_CONST;
+
+GtkWidget*
+load_graph_get_mem_color_picker(LoadGraph *g) G_GNUC_CONST;
+
+GtkWidget*
+load_graph_get_swap_color_picker(LoadGraph *g) G_GNUC_CONST;
+
+
+#endif /* _PROCMAN_LOAD_GRAPH_H_ */
diff --git a/src/lsof.cpp b/src/lsof.cpp
new file mode 100644
index 0000000..24f14a5
--- /dev/null
+++ b/src/lsof.cpp
@@ -0,0 +1,408 @@
+#include <config.h>
+
+#include <gtkmm/messagedialog.h>
+#include <glib/gi18n.h>
+#include <glibtop/procopenfiles.h>
+
+#include <sys/wait.h>
+
+// #include <libsexy/sexy-icon-entry.h>
+
+
+#include <set>
+#include <string>
+#include <sstream>
+#include <iterator>
+
+#include "regex.h"
+
+#include "procman.h"
+#include "lsof.h"
+#include "util.h"
+
+
+using std::string;
+
+
+namespace
+{
+
+ class Lsof
+ {
+ Glib::RefPtr<Glib::Regex> re;
+
+ bool matches(const string &filename) const
+ {
+ return this->re->match(filename);
+ }
+
+ public:
+
+ Lsof(const string &pattern, bool caseless)
+ {
+ Glib::RegexCompileFlags flags = static_cast<Glib::RegexCompileFlags>(0);
+
+ if (caseless)
+ flags |= Glib::REGEX_CASELESS;
+
+ this->re = Glib::Regex::create(pattern, flags);
+ }
+
+
+ template<typename OutputIterator>
+ void search(const ProcInfo &info, OutputIterator out) const
+ {
+ glibtop_open_files_entry *entries;
+ glibtop_proc_open_files buf;
+
+ entries = glibtop_get_proc_open_files(&buf, info.pid);
+
+ for (unsigned i = 0; i != buf.number; ++i) {
+ if (entries[i].type & GLIBTOP_FILE_TYPE_FILE) {
+ const string filename(entries[i].info.file.name);
+ if (this->matches(filename))
+ *out++ = filename;
+ }
+ }
+
+ g_free(entries);
+ }
+ };
+
+
+
+ // GUI Stuff
+
+
+ enum ProcmanLsof {
+ PROCMAN_LSOF_COL_PIXBUF,
+ PROCMAN_LSOF_COL_PROCESS,
+ PROCMAN_LSOF_COL_PID,
+ PROCMAN_LSOF_COL_FILENAME,
+ PROCMAN_LSOF_NCOLS
+ };
+
+
+ struct GUI {
+
+ GtkListStore *model;
+ GtkEntry *entry;
+ GtkWindow *window;
+ GtkLabel *count;
+ ProcData *procdata;
+ bool case_insensitive;
+
+
+ GUI()
+ {
+ procman_debug("New Lsof GUI %p", this);
+ }
+
+
+ ~GUI()
+ {
+ procman_debug("Destroying Lsof GUI %p", this);
+ }
+
+
+ void clear_results()
+ {
+ gtk_list_store_clear(this->model);
+ gtk_label_set_text(this->count, "");
+ }
+
+
+ void clear()
+ {
+ this->clear_results();
+ gtk_entry_set_text(this->entry, "");
+ }
+
+
+ void display_regex_error(const Glib::RegexError& error)
+ {
+ const char * msg = _("<b>Error</b>\n"
+ "'%s' is not a valid Perl regular expression.\n"
+ "%s");
+ std::string message = make_string(g_strdup_printf(msg, this->pattern().c_str(), error.what().c_str()));
+
+ Gtk::MessageDialog dialog(message,
+ true, // use markup
+ Gtk::MESSAGE_ERROR,
+ Gtk::BUTTONS_OK,
+ true); // modal
+ dialog.run();
+ }
+
+
+ void update_count(unsigned count)
+ {
+ string s = static_cast<std::ostringstream&>(std::ostringstream() << count).str();
+ gtk_label_set_text(this->count, s.c_str());
+ }
+
+
+ string pattern() const
+ {
+ return gtk_entry_get_text(this->entry);
+ }
+
+
+ void search()
+ {
+ typedef std::set<string> MatchSet;
+ typedef MatchSet::const_iterator iterator;
+
+ this->clear_results();
+
+
+ try {
+ Lsof lsof(this->pattern(), this->case_insensitive);
+
+ unsigned count = 0;
+
+ for (ProcInfo::Iterator it(ProcInfo::begin()); it != ProcInfo::end(); ++it) {
+ const ProcInfo &info(*it->second);
+
+ MatchSet matches;
+ lsof.search(info, std::inserter(matches, matches.begin()));
+ count += matches.size();
+
+ for (iterator it(matches.begin()), end(matches.end()); it != end; ++it) {
+ GtkTreeIter file;
+ gtk_list_store_append(this->model, &file);
+ gtk_list_store_set(this->model, &file,
+ PROCMAN_LSOF_COL_PIXBUF, info.pixbuf->gobj(),
+ PROCMAN_LSOF_COL_PROCESS, info.name,
+ PROCMAN_LSOF_COL_PID, info.pid,
+ PROCMAN_LSOF_COL_FILENAME, it->c_str(),
+ -1);
+ }
+ }
+
+ this->update_count(count);
+ }
+ catch (Glib::RegexError& error) {
+ this->display_regex_error(error);
+ }
+ }
+
+
+ static void search_button_clicked(GtkButton *, gpointer data)
+ {
+ static_cast<GUI*>(data)->search();
+ }
+
+
+ static void search_entry_activate(GtkEntry *, gpointer data)
+ {
+ static_cast<GUI*>(data)->search();
+ }
+
+
+ static void clear_button_clicked(GtkButton *, gpointer data)
+ {
+ static_cast<GUI*>(data)->clear();
+ }
+
+
+ static void close_button_clicked(GtkButton *, gpointer data)
+ {
+ GUI *gui = static_cast<GUI*>(data);
+ gtk_widget_destroy(GTK_WIDGET(gui->window));
+ delete gui;
+ }
+
+
+ static void case_button_toggled(GtkToggleButton *button, gpointer data)
+ {
+ bool state = gtk_toggle_button_get_active(button);
+ static_cast<GUI*>(data)->case_insensitive = state;
+ }
+
+
+ static gboolean window_delete_event(GtkWidget *, GdkEvent *, gpointer data)
+ {
+ delete static_cast<GUI*>(data);
+ return FALSE;
+ }
+
+ };
+}
+
+
+
+
+void procman_lsof(ProcData *procdata)
+{
+ GtkListStore *model = \
+ gtk_list_store_new(PROCMAN_LSOF_NCOLS,
+ GDK_TYPE_PIXBUF, // PROCMAN_LSOF_COL_PIXBUF
+ G_TYPE_STRING, // PROCMAN_LSOF_COL_PROCESS
+ G_TYPE_UINT, // PROCMAN_LSOF_COL_PID
+ G_TYPE_STRING // PROCMAN_LSOF_COL_FILENAME
+ );
+
+ GtkWidget *tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
+ g_object_unref(model);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
+
+
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+
+ // PIXBUF / PROCESS
+
+ column = gtk_tree_view_column_new();
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "pixbuf", PROCMAN_LSOF_COL_PIXBUF,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", PROCMAN_LSOF_COL_PROCESS,
+ NULL);
+
+ gtk_tree_view_column_set_title(column, _("Process"));
+ gtk_tree_view_column_set_sort_column_id(column, PROCMAN_LSOF_COL_PROCESS);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
+ gtk_tree_view_column_set_min_width(column, 10);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), PROCMAN_LSOF_COL_PROCESS,
+ GTK_SORT_ASCENDING);
+
+
+ // PID
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("PID"), renderer,
+ "text", PROCMAN_LSOF_COL_PID,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PROCMAN_LSOF_COL_PID);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+
+ // FILENAME
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Filename"), renderer,
+ "text", PROCMAN_LSOF_COL_FILENAME,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PROCMAN_LSOF_COL_FILENAME);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+
+ GtkWidget *dialog; /* = gtk_dialog_new_with_buttons(_("Search for Open Files"), NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL); */
+ dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(procdata->app));
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
+ // gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+ gtk_window_set_title(GTK_WINDOW(dialog), _("Search for Open Files"));
+
+ // g_signal_connect(G_OBJECT(dialog), "response",
+ // G_CALLBACK(close_dialog), NULL);
+ gtk_window_set_resizable(GTK_WINDOW(dialog), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(dialog), 575, 400);
+ // gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
+ GtkWidget *mainbox = gtk_vbox_new(FALSE, 12);
+ gtk_container_add(GTK_CONTAINER(dialog), mainbox);
+ gtk_box_set_spacing(GTK_BOX(mainbox), 6);
+
+
+ // Label, entry and search button
+
+ GtkWidget *hbox1 = gtk_hbox_new(FALSE, 12);
+ gtk_box_pack_start(GTK_BOX(mainbox), hbox1, FALSE, FALSE, 0);
+
+ GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start(GTK_BOX(hbox1), image, FALSE, FALSE, 0);
+
+
+ GtkWidget *vbox2 = gtk_vbox_new(FALSE, 12);
+ gtk_box_pack_start(GTK_BOX(hbox1), vbox2, TRUE, TRUE, 0);
+
+
+ GtkWidget *hbox = gtk_hbox_new(FALSE, 12);
+ gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, TRUE, 0);
+ GtkWidget *label = gtk_label_new_with_mnemonic(_("_Name contains:"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ GtkWidget *entry = gtk_entry_new();
+
+ // entry = sexy_icon_entry_new();
+ // sexy_icon_entry_add_clear_button(SEXY_ICON_ENTRY(entry));
+ // GtkWidget *icon = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
+ // sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(entry), SEXY_ICON_ENTRY_PRIMARY, GTK_IMAGE(icon));
+
+ gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+ GtkWidget *search_button = gtk_button_new_from_stock(GTK_STOCK_FIND);
+ gtk_box_pack_start(GTK_BOX(hbox), search_button, FALSE, FALSE, 0);
+ GtkWidget *clear_button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+ gtk_box_pack_start(GTK_BOX(hbox), clear_button, FALSE, FALSE, 0);
+
+
+ GtkWidget *case_button = gtk_check_button_new_with_mnemonic(_("Case insensitive matching"));
+ GtkWidget *hbox3 = gtk_hbox_new(FALSE, 12);
+ gtk_box_pack_start(GTK_BOX(hbox3), case_button, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox2), hbox3, FALSE, FALSE, 0);
+
+
+ GtkWidget *results_box = gtk_hbox_new(FALSE, 12);
+ gtk_box_pack_start(GTK_BOX(mainbox), results_box, FALSE, FALSE, 0);
+ GtkWidget *results_label = gtk_label_new_with_mnemonic(_("S_earch results:"));
+ gtk_box_pack_start(GTK_BOX(results_box), results_label, FALSE, FALSE, 0);
+ GtkWidget *count_label = gtk_label_new(NULL);
+ gtk_box_pack_end(GTK_BOX(results_box), count_label, FALSE, FALSE, 0);
+
+
+
+
+ // Scrolled TreeView
+ GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
+ GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(scrolled), tree);
+ gtk_box_pack_start(GTK_BOX(mainbox), scrolled, TRUE, TRUE, 0);
+
+ GtkWidget *bottom_box = gtk_hbox_new(FALSE, 12);
+ GtkWidget *close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start(GTK_BOX(mainbox), bottom_box, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(bottom_box), close_button, FALSE, FALSE, 0);
+
+
+ GUI *gui = new GUI; // wil be deleted by the close button or delete-event
+ gui->procdata = procdata;
+ gui->model = model;
+ gui->window = GTK_WINDOW(dialog);
+ gui->entry = GTK_ENTRY(entry);
+ gui->count = GTK_LABEL(count_label);
+
+ g_signal_connect(G_OBJECT(entry), "activate",
+ G_CALLBACK(GUI::search_entry_activate), gui);
+ g_signal_connect(G_OBJECT(clear_button), "clicked",
+ G_CALLBACK(GUI::clear_button_clicked), gui);
+ g_signal_connect(G_OBJECT(search_button), "clicked",
+ G_CALLBACK(GUI::search_button_clicked), gui);
+ g_signal_connect(G_OBJECT(close_button), "clicked",
+ G_CALLBACK(GUI::close_button_clicked), gui);
+ g_signal_connect(G_OBJECT(case_button), "toggled",
+ G_CALLBACK(GUI::case_button_toggled), gui);
+ g_signal_connect(G_OBJECT(dialog), "delete-event",
+ G_CALLBACK(GUI::window_delete_event), gui);
+
+
+ gtk_widget_show_all(dialog);
+}
+
diff --git a/src/lsof.h b/src/lsof.h
new file mode 100644
index 0000000..ad7f111
--- /dev/null
+++ b/src/lsof.h
@@ -0,0 +1,9 @@
+#ifndef H_PROCMAN_LSOF_1161179202
+#define H_PROCMAN_LSOF_1161179202
+
+#include <glib/gmacros.h>
+#include "procman.h"
+
+void procman_lsof(ProcData *data);
+
+#endif /* H_PROCMAN_LSOF_1161179202 */
diff --git a/src/mate-system-monitor.schemas.in b/src/mate-system-monitor.schemas.in
new file mode 100644
index 0000000..34bb102
--- /dev/null
+++ b/src/mate-system-monitor.schemas.in
@@ -0,0 +1,769 @@
+<mateconfschemafile>
+
+ <schemalist>
+ <schema>
+ <key>/schemas/apps/procman/width</key>
+ <applyto>/apps/procman/width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>440</default>
+ <locale name="C">
+ <short>Main Window width</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/height</key>
+ <applyto>/apps/procman/height</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>495</default>
+ <locale name="C">
+ <short>Main Window height</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/show_tree</key>
+ <applyto>/apps/procman/show_tree</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process dependencies in tree form</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/solaris_mode</key>
+ <applyto>/apps/procman/solaris_mode</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Solaris mode for CPU percentage</short>
+ <long>If TRUE, system-monitor operates in 'Solaris mode' where a task's cpu usage is divided by the total number of CPUs. Else it operates in 'Irix mode'.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/smooth_refresh</key>
+ <applyto>/apps/procman/smooth_refresh</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Enable/Disable smooth refresh</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/kill_dialog</key>
+ <applyto>/apps/procman/kill_dialog</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Show warning dialog when killing processes</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/update_interval</key>
+ <applyto>/apps/procman/update_interval</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>3000</default>
+ <locale name="C">
+ <short>Time in milliseconds between updates of the process view</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/graph_update_interval</key>
+ <applyto>/apps/procman/graph_update_interval</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>1000</default>
+ <locale name="C">
+ <short>Time in milliseconds between updates of the graphs</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/show_all_fs</key>
+ <applyto>/apps/procman/show_all_fs</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>False</default>
+ <locale name="C">
+ <short>Whether information about all filesystems should be displayed</short>
+ <long>Whether to display information about all filesystems (including types like 'autofs' and 'procfs'). Useful for getting a list of all currently mounted filesystems.</long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/disks_interval</key>
+ <applyto>/apps/procman/disks_interval</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>5000</default>
+ <locale name="C">
+ <short>Time in milliseconds between updates of the devices list</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/view_as</key>
+ <applyto>/apps/procman/view_as</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>1</default>
+ <locale name="C">
+ <short>Determines which processes to show by default. 0 is All, 1 is user, and 2 is active</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/current_tab</key>
+ <applyto>/apps/procman/current_tab</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>2</default>
+ <locale name="C">
+ <short>Saves the currently viewed tab</short>
+ <long>0 for the System Info, 1 for the processes list, 2 for the resources and 3 for the disks list</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/cpu_color0</key>
+ <applyto>/apps/procman/cpu_color0</applyto>
+ <owner>procman</owner>
+ <type>string</type>
+ <default>#FF6E00</default>
+ <locale name="C">
+ <short>Default graph cpu color</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/cpu_color1</key>
+ <applyto>/apps/procman/cpu_color1</applyto>
+ <owner>procman</owner>
+ <type>string</type>
+ <default>#CB0C29</default>
+ <locale name="C">
+ <short>Default graph cpu color</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/cpu_color2</key>
+ <applyto>/apps/procman/cpu_color2</applyto>
+ <owner>procman</owner>
+ <type>string</type>
+ <default>#49A835</default>
+ <locale name="C">
+ <short>Default graph cpu color</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/cpu_color3</key>
+ <applyto>/apps/procman/cpu_color3</applyto>
+ <owner>procman</owner>
+ <type>string</type>
+ <default>#2D7DB3</default>
+ <locale name="C">
+ <short>Default graph cpu color</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/mem_color</key>
+ <applyto>/apps/procman/mem_color</applyto>
+ <owner>procman</owner>
+ <type>string</type>
+ <default>#AB1852</default>
+ <locale name="C">
+ <short>Default graph mem color</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/swap_color</key>
+ <applyto>/apps/procman/swap_color</applyto>
+ <owner>procman</owner>
+ <type>string</type>
+ <default>#49A835</default>
+ <locale name="C">
+ <short>Default graph swap color</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/net_in_color</key>
+ <applyto>/apps/procman/net_in_color</applyto>
+ <owner>procman</owner>
+ <type>string</type>
+ <default>#2D7DB3</default>
+ <locale name="C">
+ <short>Default graph incoming network traffic color</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/net_out_color</key>
+ <applyto>/apps/procman/net_out_color</applyto>
+ <owner>procman</owner>
+ <type>string</type>
+ <default>#844798</default>
+ <locale name="C">
+ <short>Default graph outgoing network traffic color</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/sort_col</key>
+ <applyto>/apps/procman/proctree/sort_col</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Process view sort column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/columns_order</key>
+ <applyto>/apps/procman/proctree/columns_order</applyto>
+ <owner>procman</owner>
+ <type>list</type>
+ <list_type>int</list_type>
+ <locale name="C">
+ <short>Process view columns order</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/sort_order</key>
+ <applyto>/apps/procman/proctree/sort_order</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Process view sort order</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_0_width</key>
+ <applyto>/apps/procman/proctree/col_0_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>198</default>
+ <locale name="C">
+ <short>Width of process 'name' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_0_visible</key>
+ <applyto>/apps/procman/proctree/col_0_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Show process 'name' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_1_width</key>
+ <applyto>/apps/procman/proctree/col_1_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Width of process 'owner' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_1_visible</key>
+ <applyto>/apps/procman/proctree/col_1_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'owner' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_2_width</key>
+ <applyto>/apps/procman/proctree/col_2_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>37</default>
+ <locale name="C">
+ <short>Width of process 'status' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_2_visible</key>
+ <applyto>/apps/procman/proctree/col_2_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Show process 'status' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_3_width</key>
+ <applyto>/apps/procman/proctree/col_3_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Width of process 'virtual memory' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_3_visible</key>
+ <applyto>/apps/procman/proctree/col_3_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'virtual memory' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_4_width</key>
+ <applyto>/apps/procman/proctree/col_4_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>53</default>
+ <locale name="C">
+ <short>Width of process 'resident memory' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_4_visible</key>
+ <applyto>/apps/procman/proctree/col_4_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'resident memory' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_5_width</key>
+ <applyto>/apps/procman/proctree/col_5_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Width of process 'writable memory' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_5_visible</key>
+ <applyto>/apps/procman/proctree/col_5_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'writable memory' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_6_width</key>
+ <applyto>/apps/procman/proctree/col_6_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Width of process 'shared memory' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_6_visible</key>
+ <applyto>/apps/procman/proctree/col_6_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'shared memory' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_7_width</key>
+ <applyto>/apps/procman/proctree/col_7_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Width of process 'X server memory' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_7_visible</key>
+ <applyto>/apps/procman/proctree/col_7_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'X server memory' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_8_width</key>
+ <applyto>/apps/procman/proctree/col_8_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Width of process 'CPU %' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_8_visible</key>
+ <applyto>/apps/procman/proctree/col_8_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Show process 'CPU %' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_9_width</key>
+ <applyto>/apps/procman/proctree/col_9_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>50</default>
+ <locale name="C">
+ <short>Width of process 'CPU time' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_9_visible</key>
+ <applyto>/apps/procman/proctree/col_9_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'CPU time' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_10_width</key>
+ <applyto>/apps/procman/proctree/col_10_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Width of process 'start time' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_10_visible</key>
+ <applyto>/apps/procman/proctree/col_10_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'start time' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_11_width</key>
+ <applyto>/apps/procman/proctree/col_11_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>48</default>
+ <locale name="C">
+ <short>Width of process 'nice' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_11_visible</key>
+ <applyto>/apps/procman/proctree/col_11_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Show process 'nice' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_12_width</key>
+ <applyto>/apps/procman/proctree/col_12_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>48</default>
+ <locale name="C">
+ <short>Width of process 'PID' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_12_visible</key>
+ <applyto>/apps/procman/proctree/col_12_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Show process 'PID' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_13_width</key>
+ <applyto>/apps/procman/proctree/col_13_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>48</default>
+ <locale name="C">
+ <short>Width of process 'SELinux security context' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_13_visible</key>
+ <applyto>/apps/procman/proctree/col_13_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'SELinux security context' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_14_width</key>
+ <applyto>/apps/procman/proctree/col_14_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>48</default>
+ <locale name="C">
+ <short>Width of process 'arguments' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_14_visible</key>
+ <applyto>/apps/procman/proctree/col_14_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Show process 'arguments' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_15_width</key>
+ <applyto>/apps/procman/proctree/col_15_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>48</default>
+ <locale name="C">
+ <short>Width of process 'estimated memory usage' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_15_visible</key>
+ <applyto>/apps/procman/proctree/col_15_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Show process 'estimated memory usage' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_16_width</key>
+ <applyto>/apps/procman/proctree/col_16_width</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>48</default>
+ <locale name="C">
+ <short>Width of process 'Waiting Channel' column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/proctree/col_16_visible</key>
+ <applyto>/apps/procman/proctree/col_16_visible</applyto>
+ <owner>procman</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Show process 'Waiting Channel' column on startup</short>
+ <long></long>
+ </locale>
+ </schema>
+
+
+ <schema>
+ <key>/schemas/apps/procman/disktreenew/sort_col</key>
+ <applyto>/apps/procman/disktreenew/sort_col</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>1</default>
+ <locale name="C">
+ <short>Process view sort column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/disktreenew/sort_order</key>
+ <applyto>/apps/procman/disktreenew/sort_order</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Process view sort order</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/disktreenew/columns_order</key>
+ <applyto>/apps/procman/disktreenew/columns_order</applyto>
+ <owner>procman</owner>
+ <type>list</type>
+ <list_type>int</list_type>
+ <locale name="C">
+ <short>Disk view columns order</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/memmapstree/sort_col</key>
+ <applyto>/apps/procman/memmapstree/sort_col</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Process view sort column</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/procman/memmapstree/sort_order</key>
+ <applyto>/apps/procman/memmapstree/sort_order</applyto>
+ <owner>procman</owner>
+ <type>int</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Process view sort order</short>
+ <long></long>
+ </locale>
+ </schema>
+
+ </schemalist>
+
+</mateconfschemafile>
diff --git a/src/mateconf-keys.cpp b/src/mateconf-keys.cpp
new file mode 100644
index 0000000..8fbb379
--- /dev/null
+++ b/src/mateconf-keys.cpp
@@ -0,0 +1,14 @@
+#include "mateconf-keys.h"
+
+
+namespace procman
+{
+ namespace mateconf
+ {
+ const std::string root("/apps/procman");
+ const std::string solaris_mode(root + "/solaris_mode");
+ const std::string open_files_tree_prefix(root + "/openfilestree");
+ const std::string network_in_bits(root + "/network_in_bits");
+ }
+}
+
diff --git a/src/mateconf-keys.h b/src/mateconf-keys.h
new file mode 100644
index 0000000..400065c
--- /dev/null
+++ b/src/mateconf-keys.h
@@ -0,0 +1,19 @@
+#ifndef H_PROCMAN_MATECONF_KEYS_1177430397
+#define H_PROCMAN_MATECONF_KEYS_1177430397
+
+#include <string>
+
+
+namespace procman
+{
+ namespace mateconf
+ {
+ extern const std::string root;
+ extern const std::string solaris_mode;
+ extern const std::string open_files_tree_prefix;
+ extern const std::string network_in_bits;
+ }
+}
+
+
+#endif // H_PROCMAN_MATECONF_KEYS_1177430397
diff --git a/src/memmaps.cpp b/src/memmaps.cpp
new file mode 100644
index 0000000..3ca8229
--- /dev/null
+++ b/src/memmaps.cpp
@@ -0,0 +1,663 @@
+#include <config.h>
+
+#include <glibtop/procmap.h>
+#include <glibtop/mountlist.h>
+#include <sys/stat.h>
+#include <glib/gi18n.h>
+
+#include <string>
+#include <map>
+#include <sstream>
+#include <iomanip>
+#include <stdexcept>
+
+using std::string;
+
+
+#include "procman.h"
+#include "memmaps.h"
+#include "proctable.h"
+#include "util.h"
+
+
+/* be careful with this enum, you could break the column names */
+enum
+{
+ MMAP_COL_FILENAME,
+ MMAP_COL_VMSTART,
+ MMAP_COL_VMEND,
+ MMAP_COL_VMSZ,
+ MMAP_COL_FLAGS,
+ MMAP_COL_VMOFFSET,
+ MMAP_COL_PRIVATE_CLEAN,
+ MMAP_COL_PRIVATE_DIRTY,
+ MMAP_COL_SHARED_CLEAN,
+ MMAP_COL_SHARED_DIRTY,
+ MMAP_COL_DEVICE,
+ MMAP_COL_INODE,
+ MMAP_COL_MAX
+};
+
+
+namespace
+{
+ class OffsetFormater
+ {
+ string format;
+
+ public:
+
+ void set(const glibtop_map_entry &last_map)
+ {
+ this->format = (last_map.end <= G_MAXUINT32) ? "%08" G_GINT64_MODIFIER "x" : "%016" G_GINT64_MODIFIER "x";
+ }
+
+ string operator()(guint64 v) const
+ {
+ char buffer[17];
+ g_snprintf(buffer, sizeof buffer, this->format.c_str(), v);
+ return buffer;
+ }
+ };
+
+
+
+
+#if 0
+
+ struct ColumnState
+ {
+ unsigned visible;
+ unsigned id;
+ unsigned width;
+
+ int pack() const
+ {
+ unsigned p = 0;
+ p |= (this->visible & 0x0001) << 24;
+ p |= (this->id & 0x00ff) << 16;
+ p |= (this->width & 0xffff);
+ return p;
+ }
+
+ void unpack(int i)
+ {
+ this->visible = 0x0001 & (i >> 24);
+ this->id = 0x00ff & (i >> 16);
+ this->width = 0xffff & i;
+ }
+ };
+
+
+ void
+ procman_save_tree_state2(MateConfClient *client, GtkWidget *tree, const gchar *cprefix)
+ {
+ const string prefix(cprefix);
+
+ GtkTreeModel *model;
+ gint sort_col;
+ GtkSortType order;
+
+ g_assert(tree);
+ g_assert(prefix != "");
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW (tree));
+
+ if (gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model), &sort_col, &order)) {
+ mateconf_client_set_int(client, (prefix + "/sort_col").c_str(), sort_col, 0);
+ mateconf_client_set_int(client, (prefix + "/sort_order").c_str(), order, 0);
+ }
+
+ GList * const columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(tree));
+
+ GSList *list = 0;
+
+ for (GList *it = columns; it; it = it->next)
+ {
+ GtkTreeViewColumn *column;
+ ColumnState cs;
+
+ column = static_cast<GtkTreeViewColumn*>(it->data);
+ cs.id = gtk_tree_view_column_get_sort_column_id(column);
+ cs.visible = gtk_tree_view_column_get_visible(column);
+ cs.width = gtk_tree_view_column_get_width(column);
+
+ list = g_slist_append(list, GINT_TO_POINTER(cs.pack()));
+ }
+
+ g_list_free(columns);
+
+ GError *error = 0;
+
+ if (not mateconf_client_set_list(client, (prefix + "/columns").c_str(),
+ MATECONF_VALUE_INT, list,
+ &error)) {
+ g_critical("Failed to save tree state %s : %s",
+ prefix.c_str(),
+ error->message);
+ g_error_free(error);
+ }
+
+ g_slist_free(list);
+ }
+
+
+ gboolean procman_get_tree_state2(MateConfClient *client, GtkWidget *tree, const gchar *cprefix)
+ {
+ const string prefix(cprefix);
+ GtkTreeModel *model;
+
+ gint sort_col;
+ GtkSortType order;
+
+ g_assert(tree);
+ g_assert(prefix != "");
+
+ if (!mateconf_client_dir_exists(client, prefix.c_str(), 0))
+ return FALSE;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree));
+
+ sort_col = mateconf_client_get_int(client, (prefix + "/sort_col").c_str(), 0);
+ sort_order = mateconf_client_get_int(client, (prefix + "/sort_order").c_str(), 0);
+
+ if (sort_col != -1)
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), sort_col, order);
+
+ proctable_set_columns_order(GTK_TREE_VIEW(tree), order);
+
+ GSlist *list = mateconf_client_get_list(client, (prefix + "/columns").c_str(),
+ MATECONF_VALUE_INT, 0);
+
+
+ for (GSList *it = list; it; it = it->next) {
+ ColumnState cs;
+ cs.unpack(GPOINTER_TO_INT(it->data));
+
+ GtkTreeViewColumn *column;
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree), cs.id);
+
+ if (!column)
+ continue;
+
+ gtk_tree_view_column_set_visible(column, cs.visible);
+ if (cs.visible)
+ gtk_tree_view_column_set_fixed_width(column, MAX(10, cs.width));
+ }
+
+ g_slist_free(list);
+
+
+ GList * const columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(tree));
+
+ for (GList * it = columns; it; it = it->next)
+ {
+ GtkTreeViewColumn *column = static_cast<GtkTreeViewColumn*>(it->data);
+ unsigned id = gtk_tree_view_column_get_sort_column_id(column);
+
+ ColumnState &cs(states[id]);
+
+
+
+ key = g_strdup_printf("%s/col_%d_width", prefix, id);
+ value = mateconf_client_get (client, key, NULL);
+ g_free (key);
+
+ if (value != NULL) {
+ width = mateconf_value_get_int(value);
+ mateconf_value_free (value);
+
+ key = g_strdup_printf ("%s/col_%d_visible", prefix, id);
+ visible = mateconf_client_get_bool (client, key, NULL);
+ g_free (key);
+
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree), id);
+ if(!column) continue;
+ gtk_tree_view_column_set_visible (column, visible);
+ if (visible) {
+ /* ensure column is really visible */
+ width = MAX(width, 10);
+ gtk_tree_view_column_set_fixed_width(column, width);
+ }
+ }
+ }
+
+ g_list_free(columns);
+
+ return TRUE;
+ }
+
+
+
+#endif
+
+
+
+ class InodeDevices
+ {
+ typedef std::map<guint16, string> Map;
+ Map devices;
+
+ public:
+
+ void update()
+ {
+ this->devices.clear();
+
+ glibtop_mountlist list;
+ glibtop_mountentry *entries = glibtop_get_mountlist(&list, 1);
+
+ for (unsigned i = 0; i != list.number; ++i) {
+ struct stat buf;
+
+ if (stat(entries[i].devname, &buf) != -1)
+ this->devices[buf.st_rdev] = entries[i].devname;
+ }
+
+ g_free(entries);
+ }
+
+ string get(guint64 dev64)
+ {
+ if (dev64 == 0)
+ return "";
+
+ guint16 dev = dev64 & 0xffff;
+
+ if (dev != dev64)
+ g_warning("weird device %" G_GINT64_MODIFIER "x", dev64);
+
+ Map::iterator it(this->devices.find(dev));
+
+ if (it != this->devices.end())
+ return it->second;
+
+ guint8 major, minor;
+ major = dev >> 8;
+ minor = dev;
+
+ std::ostringstream out;
+ out << std::hex
+ << std::setfill('0')
+ << std::setw(2) << unsigned(major)
+ << ':'
+ << std::setw(2) << unsigned(minor);
+
+ this->devices[dev] = out.str();
+ return out.str();
+ }
+ };
+
+
+ class MemMapsData
+ {
+ public:
+ guint timer;
+ GtkWidget *tree;
+ MateConfClient *client;
+ ProcInfo *info;
+ OffsetFormater format;
+ mutable InodeDevices devices;
+ const char * const key;
+
+ MemMapsData(GtkWidget *a_tree, MateConfClient *a_client)
+ : tree(a_tree),
+ client(a_client),
+ key("/apps/procman/memmapstree2")
+ {
+ procman_get_tree_state(this->client, this->tree, this->key);
+ }
+
+ ~MemMapsData()
+ {
+ procman_save_tree_state(this->client, this->tree, this->key);
+ }
+ };
+}
+
+
+struct glibtop_map_entry_cmp
+{
+ bool operator()(const glibtop_map_entry &a, const guint64 start) const
+ {
+ return a.start < start;
+ }
+
+ bool operator()(const guint64 &start, const glibtop_map_entry &a) const
+ {
+ return not (*this)(a, start);
+ }
+
+};
+
+
+static void
+update_row(GtkTreeModel *model, GtkTreeIter &row, const MemMapsData &mm, const glibtop_map_entry *memmaps)
+{
+ guint64 size;
+ string filename, device;
+ string vmstart, vmend, vmoffset;
+ char flags[5] = "----";
+
+ size = memmaps->end - memmaps->start;
+
+ if(memmaps->perm & GLIBTOP_MAP_PERM_READ) flags [0] = 'r';
+ if(memmaps->perm & GLIBTOP_MAP_PERM_WRITE) flags [1] = 'w';
+ if(memmaps->perm & GLIBTOP_MAP_PERM_EXECUTE) flags [2] = 'x';
+ if(memmaps->perm & GLIBTOP_MAP_PERM_SHARED) flags [3] = 's';
+ if(memmaps->perm & GLIBTOP_MAP_PERM_PRIVATE) flags [3] = 'p';
+
+ if (memmaps->flags & (1 << GLIBTOP_MAP_ENTRY_FILENAME))
+ filename = memmaps->filename;
+
+ vmstart = mm.format(memmaps->start);
+ vmend = mm.format(memmaps->end);
+ vmoffset = mm.format(memmaps->offset);
+ device = mm.devices.get(memmaps->device);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &row,
+ MMAP_COL_FILENAME, filename.c_str(),
+ MMAP_COL_VMSTART, vmstart.c_str(),
+ MMAP_COL_VMEND, vmend.c_str(),
+ MMAP_COL_VMSZ, size,
+ MMAP_COL_FLAGS, flags,
+ MMAP_COL_VMOFFSET, vmoffset.c_str(),
+ MMAP_COL_PRIVATE_CLEAN, memmaps->private_clean,
+ MMAP_COL_PRIVATE_DIRTY, memmaps->private_dirty,
+ MMAP_COL_SHARED_CLEAN, memmaps->shared_clean,
+ MMAP_COL_SHARED_DIRTY, memmaps->shared_dirty,
+ MMAP_COL_DEVICE, device.c_str(),
+ MMAP_COL_INODE, memmaps->inode,
+ -1);
+}
+
+
+
+
+static void
+update_memmaps_dialog (MemMapsData *mmdata)
+{
+ GtkTreeModel *model;
+ glibtop_map_entry *memmaps;
+ glibtop_proc_map procmap;
+
+ memmaps = glibtop_get_proc_map (&procmap, mmdata->info->pid);
+ /* process has disappeared */
+ if(!memmaps or procmap.number == 0) return;
+
+ mmdata->format.set(memmaps[procmap.number - 1]);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (mmdata->tree));
+
+ GtkTreeIter iter;
+
+ typedef std::map<guint64, GtkTreeIter> IterCache;
+ IterCache iter_cache;
+
+ /*
+ removes the old maps and
+ also fills a cache of start -> iter in order to speed
+ up add
+ */
+
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ while (true) {
+ char *vmstart = 0;
+ guint64 start;
+ gtk_tree_model_get(model, &iter,
+ MMAP_COL_VMSTART, &vmstart,
+ -1);
+
+ try {
+ std::istringstream(vmstart) >> std::hex >> start;
+ } catch (std::logic_error &e) {
+ g_warning("Could not parse %s", vmstart);
+ start = 0;
+ }
+
+ g_free(vmstart);
+
+ bool found = std::binary_search(memmaps, memmaps + procmap.number,
+ start, glibtop_map_entry_cmp());
+
+ if (found) {
+ iter_cache[start] = iter;
+ if (!gtk_tree_model_iter_next(model, &iter))
+ break;
+ } else {
+ if (!gtk_list_store_remove(GTK_LIST_STORE(model), &iter))
+ break;
+ }
+ }
+ }
+
+ mmdata->devices.update();
+
+ /*
+ add the new maps
+ */
+
+ for (guint i = 0; i != procmap.number; i++) {
+ GtkTreeIter iter;
+ IterCache::iterator it(iter_cache.find(memmaps[i].start));
+
+ if (it != iter_cache.end())
+ iter = it->second;
+ else
+ gtk_list_store_prepend(GTK_LIST_STORE(model), &iter);
+
+ update_row(model, iter, *mmdata, &memmaps[i]);
+ }
+
+ g_free (memmaps);
+}
+
+
+
+static gboolean window_delete_event(GtkWidget *, GdkEvent *, gpointer data)
+{
+ MemMapsData * const mmdata = static_cast<MemMapsData*>(data);
+
+ g_source_remove (mmdata->timer);
+
+ delete mmdata;
+ return FALSE;
+}
+
+
+static MemMapsData*
+create_memmapsdata (ProcData *procdata)
+{
+ GtkWidget *tree;
+ GtkListStore *model;
+ guint i;
+
+ const gchar * const titles[] = {
+ N_("Filename"),
+ // xgettext: virtual memory start
+ N_("VM Start"),
+ // xgettext: virtual memory end
+ N_("VM End"),
+ // xgettext: virtual memory syze
+ N_("VM Size"),
+ N_("Flags"),
+ // xgettext: virtual memory offset
+ N_("VM Offset"),
+ // xgettext: memory that has not been modified since
+ // it has been allocated
+ N_("Private clean"),
+ // xgettext: memory that has been modified since it
+ // has been allocated
+ N_("Private dirty"),
+ // xgettext: shared memory that has not been modified
+ // since it has been allocated
+ N_("Shared clean"),
+ // xgettext: shared memory that has been modified
+ // since it has been allocated
+ N_("Shared dirty"),
+ N_("Device"),
+ N_("Inode")
+ };
+
+ model = gtk_list_store_new (MMAP_COL_MAX,
+ G_TYPE_STRING, /* MMAP_COL_FILENAME */
+ G_TYPE_STRING, /* MMAP_COL_VMSTART */
+ G_TYPE_STRING, /* MMAP_COL_VMEND */
+ G_TYPE_UINT64, /* MMAP_COL_VMSZ */
+ G_TYPE_STRING, /* MMAP_COL_FLAGS */
+ G_TYPE_STRING, /* MMAP_COL_VMOFFSET */
+ G_TYPE_UINT64, /* MMAP_COL_PRIVATE_CLEAN */
+ G_TYPE_UINT64, /* MMAP_COL_PRIVATE_DIRTY */
+ G_TYPE_UINT64, /* MMAP_COL_SHARED_CLEAN */
+ G_TYPE_UINT64, /* MMAP_COL_SHARED_DIRTY */
+ G_TYPE_STRING, /* MMAP_COL_DEVICE */
+ G_TYPE_UINT64 /* MMAP_COL_INODE */
+ );
+
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
+ g_object_unref (G_OBJECT (model));
+
+ for (i = 0; i < MMAP_COL_MAX; i++) {
+ GtkCellRenderer *cell;
+ GtkTreeViewColumn *col;
+
+ cell = gtk_cell_renderer_text_new();
+ col = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(col, cell, TRUE);
+ gtk_tree_view_column_set_title(col, _(titles[i]));
+ gtk_tree_view_column_set_resizable(col, TRUE);
+ gtk_tree_view_column_set_sort_column_id(col, i);
+ gtk_tree_view_column_set_reorderable(col, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
+
+ switch (i) {
+ case MMAP_COL_PRIVATE_CLEAN:
+ case MMAP_COL_PRIVATE_DIRTY:
+ case MMAP_COL_SHARED_CLEAN:
+ case MMAP_COL_SHARED_DIRTY:
+ case MMAP_COL_VMSZ:
+ gtk_tree_view_column_set_cell_data_func(col, cell,
+ &procman::size_cell_data_func,
+ GUINT_TO_POINTER(i),
+ NULL);
+
+ g_object_set(cell, "xalign", 1.0f, NULL);
+ break;
+
+ default:
+ gtk_tree_view_column_set_attributes(col, cell, "text", i, NULL);
+ break;
+ }
+
+
+ switch (i) {
+ case MMAP_COL_VMSTART:
+ case MMAP_COL_VMEND:
+ case MMAP_COL_FLAGS:
+ case MMAP_COL_VMOFFSET:
+ case MMAP_COL_DEVICE:
+ g_object_set(cell, "family", "monospace", NULL);
+ break;
+ }
+ }
+
+ return new MemMapsData(tree, procdata->client);
+}
+
+
+static gboolean
+memmaps_timer (gpointer data)
+{
+ MemMapsData * const mmdata = static_cast<MemMapsData*>(data);
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (mmdata->tree));
+ g_assert(model);
+
+ update_memmaps_dialog (mmdata);
+
+ return TRUE;
+}
+
+
+static void
+create_single_memmaps_dialog (GtkTreeModel *model, GtkTreePath *path,
+ GtkTreeIter *iter, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ MemMapsData *mmdata;
+ GtkWidget *memmapsdialog;
+ GtkWidget *dialog_vbox, *vbox;
+ GtkWidget *label;
+ GtkWidget *scrolled;
+ ProcInfo *info;
+
+ gtk_tree_model_get (model, iter, COL_POINTER, &info, -1);
+
+ if (!info)
+ return;
+
+ mmdata = create_memmapsdata (procdata);
+ mmdata->info = info;
+
+ memmapsdialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_transient_for(GTK_WINDOW(memmapsdialog), GTK_WINDOW(procdata->app));
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(memmapsdialog), TRUE);
+ // gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+ gtk_window_set_title(GTK_WINDOW(memmapsdialog), _("Memory Maps"));
+ gtk_window_set_resizable(GTK_WINDOW(memmapsdialog), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(memmapsdialog), 575, 400);
+ // gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(memmapsdialog), 12);
+
+ GtkWidget *mainbox = gtk_vbox_new(FALSE, 12);
+ gtk_container_add(GTK_CONTAINER(memmapsdialog), mainbox);
+
+ vbox = mainbox;
+ gtk_box_set_spacing (GTK_BOX (vbox), 2);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+
+ dialog_vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox), 5);
+ gtk_box_pack_start (GTK_BOX (vbox), dialog_vbox, TRUE, TRUE, 0);
+
+
+ label = procman_make_label_for_mmaps_or_ofiles (
+ _("_Memory maps for process \"%s\" (PID %u):"),
+ info->name,
+ info->pid);
+
+ gtk_box_pack_start (GTK_BOX (dialog_vbox), label, FALSE, TRUE, 0);
+
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_SHADOW_IN);
+
+ gtk_container_add (GTK_CONTAINER (scrolled), mmdata->tree);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), mmdata->tree);
+
+ gtk_box_pack_start (GTK_BOX (dialog_vbox), scrolled, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (memmapsdialog);
+
+ g_signal_connect(G_OBJECT(memmapsdialog), "delete-event",
+ G_CALLBACK(window_delete_event), mmdata);
+
+ mmdata->timer = g_timeout_add_seconds (5, memmaps_timer, mmdata);
+
+ update_memmaps_dialog (mmdata);
+}
+
+
+void
+create_memmaps_dialog (ProcData *procdata)
+{
+ /* TODO: do we really want to open multiple dialogs ? */
+ gtk_tree_selection_selected_foreach (procdata->selection, create_single_memmaps_dialog,
+ procdata);
+}
diff --git a/src/memmaps.h b/src/memmaps.h
new file mode 100644
index 0000000..f0a00cd
--- /dev/null
+++ b/src/memmaps.h
@@ -0,0 +1,9 @@
+#ifndef _PROCMAN_MEMMAPS_H_
+#define _PROCMAN_MEMMAPS_H_
+
+#include <glib.h>
+#include "procman.h"
+
+void create_memmaps_dialog (ProcData *procdata);
+
+#endif /* _PROCMAN_MEMMAPS_H_ */
diff --git a/src/openfiles.cpp b/src/openfiles.cpp
new file mode 100644
index 0000000..8b76858
--- /dev/null
+++ b/src/openfiles.cpp
@@ -0,0 +1,394 @@
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <glibtop/procopenfiles.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "procman.h"
+#include "openfiles.h"
+#include "proctable.h"
+#include "util.h"
+#include "mateconf-keys.h"
+
+enum
+{
+ COL_FD,
+ COL_TYPE,
+ COL_OBJECT,
+ COL_OPENFILE_STRUCT,
+ NUM_OPENFILES_COL
+};
+
+
+static const char*
+get_type_name(enum glibtop_file_type t)
+{
+ switch(t)
+ {
+ case GLIBTOP_FILE_TYPE_FILE:
+ return _("file");
+ case GLIBTOP_FILE_TYPE_PIPE:
+ return _("pipe");
+ case GLIBTOP_FILE_TYPE_INET6SOCKET:
+ return _("IPv6 network connection");
+ case GLIBTOP_FILE_TYPE_INETSOCKET:
+ return _("IPv4 network connection");
+ case GLIBTOP_FILE_TYPE_LOCALSOCKET:
+ return _("local socket");
+ default:
+ return _("unknown type");
+ }
+}
+
+
+
+static char *
+friendlier_hostname(const char *addr_str, int port)
+{
+ struct addrinfo hints = { };
+ struct addrinfo *res = NULL;
+ char hostname[NI_MAXHOST];
+ char service[NI_MAXSERV];
+ char port_str[6];
+
+ if (!addr_str[0]) return g_strdup("");
+
+ snprintf(port_str, sizeof port_str, "%d", port);
+
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if (getaddrinfo(addr_str, port_str, &hints, &res))
+ goto failsafe;
+
+ if (getnameinfo(res->ai_addr, res->ai_addrlen, hostname,
+ sizeof hostname, service, sizeof service, NI_IDN))
+ goto failsafe;
+
+ if (res) freeaddrinfo(res);
+ return g_strdup_printf("%s, TCP port %d (%s)", hostname, port, service);
+
+ failsafe:
+ if (res) freeaddrinfo(res);
+ return g_strdup_printf("%s, TCP port %d", addr_str, port);
+}
+
+
+
+static void
+add_new_files (gpointer key, gpointer value, gpointer data)
+{
+ glibtop_open_files_entry *openfiles = static_cast<glibtop_open_files_entry*>(value);
+
+ GtkTreeModel *model = static_cast<GtkTreeModel*>(data);
+ GtkTreeIter row;
+
+ char *object;
+
+ switch(openfiles->type)
+ {
+ case GLIBTOP_FILE_TYPE_FILE:
+ object = g_strdup(openfiles->info.file.name);
+ break;
+
+ case GLIBTOP_FILE_TYPE_INET6SOCKET:
+ case GLIBTOP_FILE_TYPE_INETSOCKET:
+ object = friendlier_hostname(openfiles->info.sock.dest_host,
+ openfiles->info.sock.dest_port);
+ break;
+
+ case GLIBTOP_FILE_TYPE_LOCALSOCKET:
+ object = g_strdup(openfiles->info.localsock.name);
+ break;
+
+ default:
+ object = g_strdup("");
+ }
+
+ gtk_list_store_insert (GTK_LIST_STORE (model), &row, 0);
+ gtk_list_store_set (GTK_LIST_STORE (model), &row,
+ COL_FD, openfiles->fd,
+ COL_TYPE, get_type_name(static_cast<glibtop_file_type>(openfiles->type)),
+ COL_OBJECT, object,
+ COL_OPENFILE_STRUCT, g_memdup(openfiles, sizeof(*openfiles)),
+ -1);
+
+ g_free(object);
+}
+
+static GList *old_maps = NULL;
+
+static gboolean
+classify_openfiles (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+ GHashTable *new_maps = static_cast<GHashTable*>(data);
+ GtkTreeIter *old_iter;
+ glibtop_open_files_entry *openfiles;
+ gchar *old_name;
+
+ gtk_tree_model_get (model, iter, 1, &old_name, -1);
+
+ openfiles = static_cast<glibtop_open_files_entry*>(g_hash_table_lookup (new_maps, old_name));
+ if (openfiles) {
+ g_hash_table_remove (new_maps, old_name);
+ g_free (old_name);
+ return FALSE;
+
+ }
+
+ old_iter = gtk_tree_iter_copy (iter);
+ old_maps = g_list_append (old_maps, old_iter);
+ g_free (old_name);
+ return FALSE;
+
+}
+
+
+static gboolean
+compare_open_files(gconstpointer a, gconstpointer b)
+{
+ const glibtop_open_files_entry *o1 = static_cast<const glibtop_open_files_entry *>(a);
+ const glibtop_open_files_entry *o2 = static_cast<const glibtop_open_files_entry *>(b);
+
+ /* Falta manejar los diferentes tipos! */
+ return (o1->fd == o2->fd) && (o1->type == o1->type); /* XXX! */
+}
+
+
+static void
+update_openfiles_dialog (GtkWidget *tree)
+{
+ ProcInfo *info;
+ GtkTreeModel *model;
+ glibtop_open_files_entry *openfiles;
+ glibtop_proc_open_files procmap;
+ GHashTable *new_maps;
+ guint i;
+
+ info = static_cast<ProcInfo*>(g_object_get_data (G_OBJECT (tree), "selected_info"));
+
+ if (!info)
+ return;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+
+ openfiles = glibtop_get_proc_open_files (&procmap, info->pid);
+
+ if (!openfiles)
+ return;
+
+ new_maps = static_cast<GHashTable *>(g_hash_table_new_full (g_str_hash, compare_open_files,
+ NULL, NULL));
+ for (i=0; i < procmap.number; i++)
+ g_hash_table_insert (new_maps, openfiles + i, openfiles + i);
+
+ gtk_tree_model_foreach (model, classify_openfiles, new_maps);
+
+ g_hash_table_foreach (new_maps, add_new_files, model);
+
+ while (old_maps) {
+ GtkTreeIter *iter = static_cast<GtkTreeIter*>(old_maps->data);
+ glibtop_open_files_entry *openfiles = NULL;
+
+ gtk_tree_model_get (model, iter,
+ COL_OPENFILE_STRUCT, &openfiles,
+ -1);
+
+ gtk_list_store_remove (GTK_LIST_STORE (model), iter);
+ gtk_tree_iter_free (iter);
+ g_free (openfiles);
+
+ old_maps = g_list_next (old_maps);
+
+ }
+
+ g_hash_table_destroy (new_maps);
+ g_free (openfiles);
+}
+
+static void
+close_openfiles_dialog (GtkDialog *dialog, gint id, gpointer data)
+{
+ GtkWidget *tree = static_cast<GtkWidget*>(data);
+ MateConfClient *client;
+ guint timer;
+
+ client = static_cast<MateConfClient*>(g_object_get_data (G_OBJECT (tree), "client"));
+ procman_save_tree_state (client, tree, procman::mateconf::open_files_tree_prefix.c_str());
+
+ timer = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (tree), "timer"));
+ g_source_remove (timer);
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ return ;
+}
+
+
+static GtkWidget *
+create_openfiles_tree (ProcData *procdata)
+{
+ GtkWidget *tree;
+ GtkListStore *model;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+ gint i;
+
+ const gchar * const titles[] = {
+ /* Translators: "FD" here means "File Descriptor". Please use
+ a very short translation if possible, and at most
+ 2-3 characters for it to be able to fit in the UI. */
+ N_("FD"),
+ N_("Type"),
+ N_("Object")
+ };
+
+ model = gtk_list_store_new (NUM_OPENFILES_COL,
+ G_TYPE_INT, /* FD */
+ G_TYPE_STRING, /* Type */
+ G_TYPE_STRING, /* Object */
+ G_TYPE_POINTER /* open_files_entry */
+ );
+
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
+ g_object_unref (G_OBJECT (model));
+
+ for (i = 0; i < NUM_OPENFILES_COL-1; i++) {
+ cell = gtk_cell_renderer_text_new ();
+
+ switch (i) {
+ case COL_FD:
+ g_object_set(cell, "xalign", 1.0f, NULL);
+ break;
+ }
+
+ column = gtk_tree_view_column_new_with_attributes (_(titles[i]),
+ cell,
+ "text", i,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, i);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
+ }
+
+#if 0
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model),
+ COL_VMSZ,
+ sort_ints,
+ GINT_TO_POINTER (COL_FD),
+ NULL);
+/*gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
+ 0,
+ GTK_SORT_ASCENDING);*/
+#endif
+
+ procman_get_tree_state (procdata->client, tree, procman::mateconf::open_files_tree_prefix.c_str());
+
+ return tree;
+
+}
+
+
+static gboolean
+openfiles_timer (gpointer data)
+{
+ GtkWidget *tree = static_cast<GtkWidget*>(data);
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+ g_assert(model);
+
+ update_openfiles_dialog (tree);
+
+ return TRUE;
+}
+
+
+static void
+create_single_openfiles_dialog (GtkTreeModel *model, GtkTreePath *path,
+ GtkTreeIter *iter, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ GtkWidget *openfilesdialog;
+ GtkWidget *dialog_vbox, *vbox;
+ GtkWidget *cmd_hbox;
+ GtkWidget *label;
+ GtkWidget *scrolled;
+ GtkWidget *tree;
+ ProcInfo *info;
+ guint timer;
+
+ gtk_tree_model_get (model, iter, COL_POINTER, &info, -1);
+
+ if (!info)
+ return;
+
+ openfilesdialog = gtk_dialog_new_with_buttons (_("Open Files"), NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL);
+ gtk_window_set_resizable (GTK_WINDOW (openfilesdialog), TRUE);
+ gtk_window_set_default_size (GTK_WINDOW (openfilesdialog), 575, 400);
+ gtk_dialog_set_has_separator (GTK_DIALOG (openfilesdialog), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (openfilesdialog), 5);
+
+ vbox = gtk_dialog_get_content_area (GTK_DIALOG (openfilesdialog));
+ gtk_box_set_spacing (GTK_BOX (vbox), 2);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+
+ dialog_vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox), 5);
+ gtk_box_pack_start (GTK_BOX (vbox), dialog_vbox, TRUE, TRUE, 0);
+
+ cmd_hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox), cmd_hbox, FALSE, FALSE, 0);
+
+
+ label = procman_make_label_for_mmaps_or_ofiles (
+ _("_Files opened by process \"%s\" (PID %u):"),
+ info->name,
+ info->pid);
+
+ gtk_box_pack_start (GTK_BOX (cmd_hbox),label, FALSE, FALSE, 0);
+
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_SHADOW_IN);
+
+ tree = create_openfiles_tree (procdata);
+ gtk_container_add (GTK_CONTAINER (scrolled), tree);
+ g_object_set_data (G_OBJECT (tree), "selected_info", info);
+ g_object_set_data (G_OBJECT (tree), "client", procdata->client);
+
+ gtk_box_pack_start (GTK_BOX (dialog_vbox), scrolled, TRUE, TRUE, 0);
+ gtk_widget_show_all (scrolled);
+
+ g_signal_connect (G_OBJECT (openfilesdialog), "response",
+ G_CALLBACK (close_openfiles_dialog), tree);
+
+ gtk_widget_show_all (openfilesdialog);
+
+ timer = g_timeout_add_seconds (5, openfiles_timer, tree);
+ g_object_set_data (G_OBJECT (tree), "timer", GUINT_TO_POINTER (timer));
+
+ update_openfiles_dialog (tree);
+
+}
+
+
+void
+create_openfiles_dialog (ProcData *procdata)
+{
+ gtk_tree_selection_selected_foreach (procdata->selection, create_single_openfiles_dialog,
+ procdata);
+}
diff --git a/src/openfiles.h b/src/openfiles.h
new file mode 100644
index 0000000..38b1cc8
--- /dev/null
+++ b/src/openfiles.h
@@ -0,0 +1,10 @@
+#ifndef _OPENFILES_H_
+#define _OPENFILES_H_
+
+#include <glib/gtypes.h>
+
+#include "procman.h"
+
+void create_openfiles_dialog (ProcData *procdata);
+
+#endif
diff --git a/src/prettytable.cpp b/src/prettytable.cpp
new file mode 100644
index 0000000..25f6625
--- /dev/null
+++ b/src/prettytable.cpp
@@ -0,0 +1,256 @@
+#include <config.h>
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include <libwnck/libwnck.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glibtop/procstate.h>
+
+#include <vector>
+
+#include "prettytable.h"
+#include "defaulttable.h"
+#include "proctable.h"
+#include "util.h"
+
+
+namespace
+{
+ const unsigned APP_ICON_SIZE = 16;
+}
+
+
+PrettyTable::PrettyTable()
+{
+ WnckScreen* screen = wnck_screen_get_default();
+ g_signal_connect(G_OBJECT(screen), "application_opened",
+ G_CALLBACK(PrettyTable::on_application_opened), this);
+ g_signal_connect(G_OBJECT(screen), "application_closed",
+ G_CALLBACK(PrettyTable::on_application_closed), this);
+}
+
+
+PrettyTable::~PrettyTable()
+{
+}
+
+
+void
+PrettyTable::on_application_opened(WnckScreen* screen, WnckApplication* app, gpointer data)
+{
+ PrettyTable * const that = static_cast<PrettyTable*>(data);
+
+ pid_t pid = wnck_application_get_pid(app);
+
+ if (pid == 0)
+ return;
+
+ const char* icon_name = wnck_application_get_icon_name(app);
+
+
+ Glib::RefPtr<Gdk::Pixbuf> icon;
+
+ icon = that->theme->load_icon(icon_name, APP_ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN);
+
+ if (not icon) {
+ icon = Glib::wrap(wnck_application_get_icon(app), /* take_copy */ true);
+ icon = icon->scale_simple(APP_ICON_SIZE, APP_ICON_SIZE, Gdk::INTERP_HYPER);
+ }
+
+ if (not icon)
+ return;
+
+ that->register_application(pid, icon);
+}
+
+
+
+void
+PrettyTable::register_application(pid_t pid, Glib::RefPtr<Gdk::Pixbuf> icon)
+{
+ /* If process already exists then set the icon. Otherwise put into hash
+ ** table to be added later */
+ if (ProcInfo* info = ProcInfo::find(pid))
+ {
+ info->set_icon(icon);
+ // move the ref to the map
+ this->apps[pid] = icon;
+ procman_debug("WNCK OK for %u", unsigned(pid));
+ }
+}
+
+
+
+void
+PrettyTable::on_application_closed(WnckScreen* screen, WnckApplication* app, gpointer data)
+{
+ pid_t pid = wnck_application_get_pid(app);
+
+ if (pid == 0)
+ return;
+
+ static_cast<PrettyTable*>(data)->unregister_application(pid);
+}
+
+
+
+void
+PrettyTable::unregister_application(pid_t pid)
+{
+ IconsForPID::iterator it(this->apps.find(pid));
+
+ if (it != this->apps.end())
+ this->apps.erase(it);
+}
+
+
+
+Glib::RefPtr<Gdk::Pixbuf>
+PrettyTable::get_icon_from_theme(const ProcInfo &info)
+{
+ return this->theme->load_icon(info.name, APP_ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN);
+}
+
+
+bool PrettyTable::get_default_icon_name(const string &cmd, string &name)
+{
+ for (size_t i = 0; i != G_N_ELEMENTS(default_table); ++i) {
+ if (default_table[i].command->match(cmd)) {
+ name = default_table[i].icon;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ Try to get an icon from the default_table
+ If it's not in defaults, try to load it.
+ If there is no default for a command, store NULL in defaults
+ so we don't have to lookup again.
+*/
+
+Glib::RefPtr<Gdk::Pixbuf>
+PrettyTable::get_icon_from_default(const ProcInfo &info)
+{
+ Glib::RefPtr<Gdk::Pixbuf> pix;
+ string name;
+
+ if (this->get_default_icon_name(info.name, name)) {
+ IconCache::iterator it(this->defaults.find(name));
+
+ if (it == this->defaults.end()) {
+ pix = this->theme->load_icon(name, APP_ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN);
+ if (pix)
+ this->defaults[name] = pix;
+ } else
+ pix = it->second;
+ }
+
+ return pix;
+}
+
+
+
+Glib::RefPtr<Gdk::Pixbuf>
+PrettyTable::get_icon_from_wnck(const ProcInfo &info)
+{
+ Glib::RefPtr<Gdk::Pixbuf> icon;
+
+ IconsForPID::iterator it(this->apps.find(info.pid));
+
+ if (it != this->apps.end())
+ icon = it->second;
+
+ return icon;
+}
+
+
+
+Glib::RefPtr<Gdk::Pixbuf>
+PrettyTable::get_icon_from_name(const ProcInfo &info)
+{
+ return this->theme->load_icon(info.name, APP_ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN);
+}
+
+
+Glib::RefPtr<Gdk::Pixbuf>
+PrettyTable::get_icon_dummy(const ProcInfo &)
+{
+ return this->theme->load_icon("application-x-executable", APP_ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN);
+}
+
+
+namespace
+{
+ bool has_kthreadd()
+ {
+ glibtop_proc_state buf;
+ glibtop_get_proc_state(&buf, 2);
+
+ return buf.cmd == string("kthreadd");
+ }
+
+ // @pre: has_kthreadd
+ bool is_kthread(const ProcInfo &info)
+ {
+ return info.pid == 2 or info.ppid == 2;
+ }
+}
+
+
+Glib::RefPtr<Gdk::Pixbuf>
+PrettyTable::get_icon_for_kernel(const ProcInfo &info)
+{
+ if (is_kthread(info))
+ return this->theme->load_icon("applications-system", APP_ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN);
+
+ return Glib::RefPtr<Gdk::Pixbuf>();
+}
+
+
+
+void
+PrettyTable::set_icon(ProcInfo &info)
+{
+ typedef Glib::RefPtr<Gdk::Pixbuf>
+ (PrettyTable::*Getter)(const ProcInfo &);
+
+ static std::vector<Getter> getters;
+
+ if (getters.empty())
+ {
+ getters.push_back(&PrettyTable::get_icon_from_wnck);
+ getters.push_back(&PrettyTable::get_icon_from_theme);
+ getters.push_back(&PrettyTable::get_icon_from_default);
+ getters.push_back(&PrettyTable::get_icon_from_name);
+ if (has_kthreadd())
+ {
+ procman_debug("kthreadd is running with PID 2");
+ getters.push_back(&PrettyTable::get_icon_for_kernel);
+ }
+ getters.push_back(&PrettyTable::get_icon_dummy);
+ }
+
+ Glib::RefPtr<Gdk::Pixbuf> icon;
+
+ for (size_t i = 0; not icon and i < getters.size(); ++i) {
+ try {
+ icon = (this->*getters[i])(info);
+ }
+ catch (std::exception& e) {
+ g_warning("Failed to load icon for %s(%u) : %s", info.name, info.pid, e.what());
+ continue;
+ }
+ catch (Glib::Exception& e) {
+ g_warning("Failed to load icon for %s(%u) : %s", info.name, info.pid, e.what().c_str());
+ continue;
+ }
+ }
+
+ info.set_icon(icon);
+}
+
diff --git a/src/prettytable.h b/src/prettytable.h
new file mode 100644
index 0000000..7884cdd
--- /dev/null
+++ b/src/prettytable.h
@@ -0,0 +1,62 @@
+// -*- c++ -*-
+
+#ifndef _PROCMAN_PRETTYTABLE_H_
+#define _PROCMAN_PRETTYTABLE_H_
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glibmm/refptr.h>
+#include <gdkmm/pixbuf.h>
+
+#include <map>
+#include <string>
+
+extern "C" {
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include <libwnck/libwnck.h>
+}
+
+#include "iconthemewrapper.h"
+
+class ProcInfo;
+
+using std::string;
+
+
+
+class PrettyTable
+{
+ public:
+ PrettyTable();
+ ~PrettyTable();
+
+ void set_icon(ProcInfo &);
+
+private:
+
+ static void on_application_opened(WnckScreen* screen, WnckApplication* app, gpointer data);
+ static void on_application_closed(WnckScreen* screen, WnckApplication* app, gpointer data);
+
+ void register_application(pid_t pid, Glib::RefPtr<Gdk::Pixbuf> icon);
+ void unregister_application(pid_t pid);
+
+
+ Glib::RefPtr<Gdk::Pixbuf> get_icon_from_theme(const ProcInfo &);
+ Glib::RefPtr<Gdk::Pixbuf> get_icon_from_default(const ProcInfo &);
+ Glib::RefPtr<Gdk::Pixbuf> get_icon_from_wnck(const ProcInfo &);
+ Glib::RefPtr<Gdk::Pixbuf> get_icon_from_name(const ProcInfo &);
+ Glib::RefPtr<Gdk::Pixbuf> get_icon_for_kernel(const ProcInfo &);
+ Glib::RefPtr<Gdk::Pixbuf> get_icon_dummy(const ProcInfo &);
+
+ bool get_default_icon_name(const string &cmd, string &name);
+
+ typedef std::map<string, Glib::RefPtr<Gdk::Pixbuf> > IconCache;
+ typedef std::map<pid_t, Glib::RefPtr<Gdk::Pixbuf> > IconsForPID;
+
+ IconsForPID apps;
+ IconCache defaults;
+ procman::IconThemeWrapper theme;
+};
+
+
+#endif /* _PROCMAN_PRETTYTABLE_H_ */
diff --git a/src/procactions.cpp b/src/procactions.cpp
new file mode 100644
index 0000000..8e79608
--- /dev/null
+++ b/src/procactions.cpp
@@ -0,0 +1,190 @@
+/* Procman process actions
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <config.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "procactions.h"
+#include "procman.h"
+#include "proctable.h"
+#include "procdialogs.h"
+#include "callbacks.h"
+
+
+static void
+renice_single_process (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+ const struct ReniceArgs * const args = static_cast<ReniceArgs*>(data);
+
+ ProcInfo *info = NULL;
+ gint error;
+ int saved_errno;
+ gchar *error_msg;
+ GtkWidget *dialog;
+
+ gtk_tree_model_get (model, iter, COL_POINTER, &info, -1);
+
+ if (!info)
+ return;
+
+ error = setpriority (PRIO_PROCESS, info->pid, args->nice_value);
+
+ /* success */
+ if(error != -1) return;
+
+ saved_errno = errno;
+
+ /* need to be root */
+ if(errno == EPERM || errno == EACCES) {
+ gboolean success;
+
+ success = procdialog_create_root_password_dialog (
+ PROCMAN_ACTION_RENICE, args->procdata, info->pid,
+ args->nice_value);
+
+ if(success) return;
+
+ if(errno) {
+ saved_errno = errno;
+ }
+ }
+
+ /* failed */
+ error_msg = g_strdup_printf (
+ _("Cannot change the priority of process with pid %d to %d.\n"
+ "%s"),
+ info->pid, args->nice_value, g_strerror(saved_errno));
+
+ dialog = gtk_message_dialog_new (
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s", error_msg);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ g_free (error_msg);
+}
+
+
+void
+renice (ProcData *procdata, int nice)
+{
+ struct ReniceArgs args = { procdata, nice };
+
+ /* EEEK - ugly hack - make sure the table is not updated as a crash
+ ** occurs if you first kill a process and the tree node is removed while
+ ** still in the foreach function
+ */
+ g_source_remove(procdata->timeout);
+
+ gtk_tree_selection_selected_foreach(procdata->selection, renice_single_process,
+ &args);
+
+ procdata->timeout = g_timeout_add(procdata->config.update_interval,
+ cb_timeout,
+ procdata);
+
+ proctable_update_all (procdata);
+}
+
+
+
+
+static void
+kill_single_process (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+ const struct KillArgs * const args = static_cast<KillArgs*>(data);
+ char *error_msg;
+ ProcInfo *info;
+ int error;
+ int saved_errno;
+ GtkWidget *dialog;
+
+ gtk_tree_model_get (model, iter, COL_POINTER, &info, -1);
+
+ if (!info)
+ return;
+
+ error = kill (info->pid, args->signal);
+
+ /* success */
+ if(error != -1) return;
+
+ saved_errno = errno;
+
+ /* need to be root */
+ if(errno == EPERM) {
+ gboolean success;
+
+ success = procdialog_create_root_password_dialog (
+ PROCMAN_ACTION_KILL, args->procdata, info->pid,
+ args->signal);
+
+ if(success) return;
+
+ if(errno) {
+ saved_errno = errno;
+ }
+ }
+
+ /* failed */
+ error_msg = g_strdup_printf (
+ _("Cannot kill process with pid %d with signal %d.\n"
+ "%s"),
+ info->pid, args->signal, g_strerror(saved_errno));
+
+ dialog = gtk_message_dialog_new (
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s", error_msg);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ g_free (error_msg);
+}
+
+
+void
+kill_process (ProcData *procdata, int sig)
+{
+ struct KillArgs args = { procdata, sig };
+
+ /* EEEK - ugly hack - make sure the table is not updated as a crash
+ ** occurs if you first kill a process and the tree node is removed while
+ ** still in the foreach function
+ */
+ g_source_remove (procdata->timeout);
+
+ gtk_tree_selection_selected_foreach (procdata->selection, kill_single_process,
+ &args);
+
+ procdata->timeout = g_timeout_add (procdata->config.update_interval,
+ cb_timeout,
+ procdata);
+ proctable_update_all (procdata);
+}
diff --git a/src/procactions.h b/src/procactions.h
new file mode 100644
index 0000000..a4190d5
--- /dev/null
+++ b/src/procactions.h
@@ -0,0 +1,28 @@
+/* Procman process actions
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#ifndef _PROCACTIONS_H_
+#define _PROCACTIONS_H_
+
+#include "procman.h"
+
+void renice (ProcData *procdata, int nice);
+void kill_process (ProcData *procdata, int sig);
+
+#endif
+
diff --git a/src/procdialogs.cpp b/src/procdialogs.cpp
new file mode 100644
index 0000000..fef8b5f
--- /dev/null
+++ b/src/procdialogs.cpp
@@ -0,0 +1,842 @@
+/* Procman - dialogs
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+
+#include <signal.h>
+#include <string.h>
+#include "procdialogs.h"
+#include "proctable.h"
+#include "callbacks.h"
+#include "prettytable.h"
+#include "procactions.h"
+#include "util.h"
+#include "load-graph.h"
+#include "mateconf-keys.h"
+#include "procman_matesu.h"
+#include "procman_gksu.h"
+
+static GtkWidget *renice_dialog = NULL;
+static GtkWidget *prefs_dialog = NULL;
+static gint new_nice_value = 0;
+
+
+static void
+kill_dialog_button_pressed (GtkDialog *dialog, gint id, gpointer data)
+{
+ struct KillArgs *kargs = static_cast<KillArgs*>(data);
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ if (id == GTK_RESPONSE_OK)
+ kill_process (kargs->procdata, kargs->signal);
+
+ g_free (kargs);
+}
+
+void
+procdialog_create_kill_dialog (ProcData *procdata, int signal)
+{
+ GtkWidget *kill_alert_dialog;
+ gchar *primary, *secondary, *button_text;
+ struct KillArgs *kargs;
+
+ kargs = g_new(KillArgs, 1);
+ kargs->procdata = procdata;
+ kargs->signal = signal;
+
+
+ if (signal == SIGKILL) {
+ /*xgettext: primary alert message*/
+ primary = _("Kill the selected process?");
+ /*xgettext: secondary alert message*/
+ secondary = _("Killing a process may destroy data, break the "
+ "session or introduce a security risk. "
+ "Only unresponding processes should be killed.");
+ button_text = _("_Kill Process");
+ }
+ else {
+ /*xgettext: primary alert message*/
+ primary = _("End the selected process?");
+ /*xgettext: secondary alert message*/
+ secondary = _("Ending a process may destroy data, break the "
+ "session or introduce a security risk. "
+ "Only unresponding processes should be ended.");
+ button_text = _("_End Process");
+ }
+
+ kill_alert_dialog = gtk_message_dialog_new (GTK_WINDOW (procdata->app),
+ static_cast<GtkDialogFlags>(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "%s",
+ primary);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (kill_alert_dialog),
+ "%s",
+ secondary);
+
+ gtk_dialog_add_buttons (GTK_DIALOG (kill_alert_dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ button_text, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (kill_alert_dialog),
+ GTK_RESPONSE_CANCEL);
+
+ g_signal_connect (G_OBJECT (kill_alert_dialog), "response",
+ G_CALLBACK (kill_dialog_button_pressed), kargs);
+
+ gtk_widget_show_all (kill_alert_dialog);
+}
+
+static gchar *
+get_nice_level (gint nice)
+{
+ if (nice < -7)
+ return _("(Very High Priority)");
+ else if (nice < -2)
+ return _("(High Priority)");
+ else if (nice < 3)
+ return _("(Normal Priority)");
+ else if (nice < 7)
+ return _("(Low Priority)");
+ else
+ return _("(Very Low Priority)");
+}
+
+static void
+renice_scale_changed (GtkAdjustment *adj, gpointer data)
+{
+ GtkWidget *label = GTK_WIDGET (data);
+
+ new_nice_value = int(gtk_adjustment_get_value (adj));
+ gtk_label_set_text (GTK_LABEL (label), get_nice_level (new_nice_value));
+
+}
+
+static void
+renice_dialog_button_pressed (GtkDialog *dialog, gint id, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+
+ if (id == 100) {
+ if (new_nice_value == -100)
+ return;
+ renice(procdata, new_nice_value);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ renice_dialog = NULL;
+}
+
+void
+procdialog_create_renice_dialog (ProcData *procdata)
+{
+ ProcInfo *info = procdata->selected_process;
+ GtkWidget *dialog = NULL;
+ GtkWidget *dialog_vbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *priority_label;
+ GtkWidget *table;
+ GtkObject *renice_adj;
+ GtkWidget *hscale;
+ GtkWidget *button;
+ GtkWidget *align;
+ GtkWidget *icon;
+ gchar *text;
+
+ if (renice_dialog)
+ return;
+
+ if (!info)
+ return;
+
+ dialog = gtk_dialog_new_with_buttons (_("Change Priority"), NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ renice_dialog = dialog;
+ gtk_window_set_resizable (GTK_WINDOW (renice_dialog), FALSE);
+ gtk_dialog_set_has_separator (GTK_DIALOG (renice_dialog), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (renice_dialog), 5);
+
+ button = gtk_button_new ();
+ gtk_widget_set_can_default (button, TRUE);
+
+ align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_container_add (GTK_CONTAINER (button), align);
+
+ hbox = gtk_hbox_new (FALSE, 2);
+ gtk_container_add (GTK_CONTAINER (align), hbox);
+
+ icon = gtk_image_new_from_stock (GTK_STOCK_OK, GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
+
+ label = gtk_label_new_with_mnemonic (_("Change _Priority"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), button);
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ gtk_dialog_add_action_widget (GTK_DIALOG (renice_dialog), button, 100);
+ gtk_dialog_set_default_response (GTK_DIALOG (renice_dialog), 100);
+ new_nice_value = -100;
+
+ dialog_vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_box_set_spacing (GTK_BOX (dialog_vbox), 2);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox), 5);
+
+ vbox = gtk_vbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox), vbox, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE(table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE(table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+
+ label = gtk_label_new_with_mnemonic (_("_Nice value:"));
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+
+ renice_adj = gtk_adjustment_new (info->nice, RENICE_VAL_MIN, RENICE_VAL_MAX, 1, 1, 0);
+ new_nice_value = 0;
+ hscale = gtk_hscale_new (GTK_ADJUSTMENT (renice_adj));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), hscale);
+ gtk_scale_set_digits (GTK_SCALE (hscale), 0);
+ gtk_table_attach (GTK_TABLE (table), hscale, 1, 2, 0, 1,
+ static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL), GTK_FILL, 0, 0);
+
+ priority_label = gtk_label_new (get_nice_level (info->nice));
+ gtk_table_attach (GTK_TABLE (table), priority_label, 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+
+ text = g_strconcat("<small><i><b>", _("Note:"), "</b> ",
+ _("The priority of a process is given by its nice value. A lower nice value corresponds to a higher priority."),
+ "</i></small>", NULL);
+ label = gtk_label_new (_(text));
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ g_free (text);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (renice_dialog_button_pressed), procdata);
+ g_signal_connect (G_OBJECT (renice_adj), "value_changed",
+ G_CALLBACK (renice_scale_changed), priority_label);
+
+ gtk_widget_show_all (dialog);
+
+
+}
+
+static void
+prefs_dialog_button_pressed (GtkDialog *dialog, gint id, gpointer data)
+{
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ prefs_dialog = NULL;
+}
+
+
+static void
+show_kill_dialog_toggled (GtkToggleButton *button, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfClient *client = procdata->client;
+
+ gboolean toggled;
+
+ toggled = gtk_toggle_button_get_active (button);
+
+ mateconf_client_set_bool (client, "/apps/procman/kill_dialog", toggled, NULL);
+
+}
+
+
+
+static void
+solaris_mode_toggled(GtkToggleButton *button, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfClient *client = procdata->client;
+ gboolean toggled;
+ toggled = gtk_toggle_button_get_active(button);
+ mateconf_client_set_bool(client, procman::mateconf::solaris_mode.c_str(), toggled, NULL);
+}
+
+
+static void
+network_in_bits_toggled(GtkToggleButton *button, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfClient *client = procdata->client;
+ gboolean toggled;
+ toggled = gtk_toggle_button_get_active(button);
+ mateconf_client_set_bool(client, procman::mateconf::network_in_bits.c_str(), toggled, NULL);
+}
+
+
+
+static void
+smooth_refresh_toggled(GtkToggleButton *button, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfClient *client = procdata->client;
+
+ gboolean toggled;
+
+ toggled = gtk_toggle_button_get_active(button);
+
+ mateconf_client_set_bool(client, SmoothRefresh::KEY.c_str(), toggled, NULL);
+}
+
+
+
+static void
+show_all_fs_toggled (GtkToggleButton *button, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfClient *client = procdata->client;
+
+ gboolean toggled;
+
+ toggled = gtk_toggle_button_get_active (button);
+
+ mateconf_client_set_bool (client, "/apps/procman/show_all_fs", toggled, NULL);
+}
+
+
+class SpinButtonUpdater
+{
+public:
+ SpinButtonUpdater(const string& mateconf_key)
+ : mateconf_key(mateconf_key)
+ { }
+
+ static gboolean callback(GtkWidget *widget, GdkEventFocus *event, gpointer data)
+ {
+ SpinButtonUpdater* updater = static_cast<SpinButtonUpdater*>(data);
+ updater->update(GTK_SPIN_BUTTON(widget));
+ return FALSE;
+ }
+
+private:
+
+ void update(GtkSpinButton* spin)
+ {
+ int new_value = int(1000 * gtk_spin_button_get_value(spin));
+ GError* e = 0;
+
+ if (not mateconf_client_set_int(ProcData::get_instance()->client,
+ this->mateconf_key.c_str(), new_value,
+ &e)) {
+ g_warning("Failed to mateconf_client_set_int %s %d : %s\n",
+ this->mateconf_key.c_str(), new_value, e->message);
+ g_error_free(e);
+ }
+
+ procman_debug("set %s to %d", this->mateconf_key.c_str(), new_value);
+ }
+
+ const string mateconf_key;
+};
+
+
+
+
+static void
+field_toggled (GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
+{
+ GtkTreeModel *model = static_cast<GtkTreeModel*>(data);
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
+ GtkTreeIter iter;
+ GtkTreeViewColumn *column;
+ gboolean toggled;
+
+ if (!path)
+ return;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_tree_model_get (model, &iter, 2, &column, -1);
+ toggled = gtk_cell_renderer_toggle_get_active (cell);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, !toggled, -1);
+ gtk_tree_view_column_set_visible (column, !toggled);
+
+ gtk_tree_path_free (path);
+
+}
+
+static GtkWidget *
+create_field_page(GtkWidget *tree, const char* text)
+{
+ GtkWidget *vbox;
+ GtkWidget *scrolled;
+ GtkWidget *label;
+ GtkWidget *treeview;
+ GList *it, *columns;
+ GtkListStore *model;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+
+ vbox = gtk_vbox_new (FALSE, 6);
+
+ label = gtk_label_new_with_mnemonic (text);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
+
+ model = gtk_list_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
+
+ treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+ gtk_container_add (GTK_CONTAINER (scrolled), treeview);
+ g_object_unref (G_OBJECT (model));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), treeview);
+
+ column = gtk_tree_view_column_new ();
+
+ cell = gtk_cell_renderer_toggle_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "active", 0,
+ NULL);
+ g_signal_connect (G_OBJECT (cell), "toggled", G_CALLBACK (field_toggled), model);
+ gtk_tree_view_column_set_clickable (column, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+
+ column = gtk_tree_view_column_new ();
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", 1,
+ NULL);
+
+ gtk_tree_view_column_set_title (column, "Not Shown");
+ gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
+
+ columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (tree));
+
+ for(it = columns; it; it = it->next)
+ {
+ GtkTreeViewColumn *column = static_cast<GtkTreeViewColumn*>(it->data);
+ GtkTreeIter iter;
+ const gchar *title;
+ gboolean visible;
+
+ title = gtk_tree_view_column_get_title (column);
+ if (!title)
+ title = _("Icon");
+
+ visible = gtk_tree_view_column_get_visible (column);
+
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter, 0, visible, 1, title, 2, column,-1);
+ }
+
+ g_list_free(columns);
+
+ return vbox;
+}
+
+void
+procdialog_create_preferences_dialog (ProcData *procdata)
+{
+ static GtkWidget *dialog = NULL;
+
+ typedef SpinButtonUpdater SBU;
+
+ static SBU interval_updater("/apps/procman/update_interval");
+ static SBU graph_interval_updater("/apps/procman/graph_update_interval");
+ static SBU disks_interval_updater("/apps/procman/disks_interval");
+
+ GtkWidget *notebook;
+ GtkWidget *proc_box;
+ GtkWidget *sys_box;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox, *vbox2, *vbox3;
+ GtkWidget *hbox, *hbox2, *hbox3;
+ GtkWidget *label;
+ GtkAdjustment *adjustment;
+ GtkWidget *spin_button;
+ GtkWidget *check_button;
+ GtkWidget *tab_label;
+ GtkWidget *smooth_button;
+ GtkSizeGroup *size;
+ gfloat update;
+ gchar *tmp;
+
+ if (prefs_dialog)
+ return;
+
+ size = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ dialog = gtk_dialog_new_with_buttons (_("System Monitor Preferences"),
+ GTK_WINDOW (procdata->app),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL);
+ /* FIXME: we should not declare the window size, but let it's */
+ /* driven by window childs. The problem is that the fields list */
+ /* have to show at least 4 items to respect HIG. I don't know */
+ /* any function to set list height by contents/items inside it. */
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 420);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ prefs_dialog = dialog;
+
+ main_vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_box_set_spacing (GTK_BOX (main_vbox), 2);
+
+ notebook = gtk_notebook_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (notebook), 5);
+ gtk_box_pack_start (GTK_BOX (main_vbox), notebook, TRUE, TRUE, 0);
+
+ proc_box = gtk_vbox_new (FALSE, 18);
+ gtk_container_set_border_width (GTK_CONTAINER (proc_box), 12);
+ tab_label = gtk_label_new (_("Processes"));
+ gtk_widget_show (tab_label);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), proc_box, tab_label);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (proc_box), vbox, FALSE, FALSE, 0);
+
+ tmp = g_strdup_printf ("<b>%s</b>", _("Behavior"));
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_label_set_markup (GTK_LABEL (label), tmp);
+ g_free (tmp);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
+
+ hbox2 = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0);
+
+ label = gtk_label_new_with_mnemonic (_("_Update interval in seconds:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
+
+ hbox3 = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox2), hbox3, TRUE, TRUE, 0);
+
+ update = (gfloat) procdata->config.update_interval;
+ adjustment = (GtkAdjustment *) gtk_adjustment_new(update / 1000.0,
+ MIN_UPDATE_INTERVAL / 1000,
+ MAX_UPDATE_INTERVAL / 1000,
+ 0.25,
+ 1.0,
+ 1.0);
+
+ spin_button = gtk_spin_button_new (adjustment, 1.0, 2);
+ gtk_box_pack_start (GTK_BOX (hbox3), spin_button, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (spin_button), "focus_out_event",
+ G_CALLBACK (SBU::callback), &interval_updater);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin_button);
+
+
+ hbox2 = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
+
+ smooth_button = gtk_check_button_new_with_mnemonic(_("Enable _smooth refresh"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(smooth_button),
+ mateconf_client_get_bool(procdata->client,
+ SmoothRefresh::KEY.c_str(),
+ NULL));
+ g_signal_connect(G_OBJECT(smooth_button), "toggled",
+ G_CALLBACK(smooth_refresh_toggled), procdata);
+ gtk_box_pack_start(GTK_BOX(hbox2), smooth_button, TRUE, TRUE, 0);
+
+
+
+ hbox2 = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0);
+
+ check_button = gtk_check_button_new_with_mnemonic (_("Alert before ending or _killing processes"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ procdata->config.show_kill_warning);
+ g_signal_connect (G_OBJECT (check_button), "toggled",
+ G_CALLBACK (show_kill_dialog_toggled), procdata);
+ gtk_box_pack_start (GTK_BOX (hbox2), check_button, FALSE, FALSE, 0);
+
+
+
+
+ hbox2 = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
+
+ GtkWidget *solaris_button;
+ solaris_button = gtk_check_button_new_with_mnemonic(_("Solaris mode"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(solaris_button),
+ mateconf_client_get_bool(procdata->client,
+ procman::mateconf::solaris_mode.c_str(),
+ NULL));
+ g_signal_connect(G_OBJECT(solaris_button), "toggled",
+ G_CALLBACK(solaris_mode_toggled), procdata);
+ gtk_box_pack_start(GTK_BOX(hbox2), solaris_button, TRUE, TRUE, 0);
+
+
+
+
+ hbox2 = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (proc_box), vbox, TRUE, TRUE, 0);
+
+ tmp = g_strdup_printf ("<b>%s</b>", _("Information Fields"));
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_label_set_markup (GTK_LABEL (label), tmp);
+ g_free (tmp);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ vbox2 = create_field_page (procdata->tree, _("Process i_nformation shown in list:"));
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
+
+ sys_box = gtk_vbox_new (FALSE, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (sys_box), 12);
+ tab_label = gtk_label_new (_("Resources"));
+ gtk_widget_show (tab_label);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sys_box, tab_label);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (sys_box), vbox, FALSE, FALSE, 0);
+
+ tmp = g_strdup_printf ("<b>%s</b>", _("Graphs"));
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_label_set_markup (GTK_LABEL (label), tmp);
+ g_free (tmp);
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
+
+ hbox2 = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0);
+
+ label = gtk_label_new_with_mnemonic (_("_Update interval in seconds:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
+ gtk_size_group_add_widget (size, label);
+
+ hbox3 = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox2), hbox3, TRUE, TRUE, 0);
+
+ update = (gfloat) procdata->config.graph_update_interval;
+ adjustment = (GtkAdjustment *) gtk_adjustment_new(update / 1000.0, 0.25,
+ 100.0, 0.25, 1.0, 1.0);
+ spin_button = gtk_spin_button_new (adjustment, 1.0, 2);
+ g_signal_connect (G_OBJECT (spin_button), "focus_out_event",
+ G_CALLBACK(SBU::callback),
+ &graph_interval_updater);
+ gtk_box_pack_start (GTK_BOX (hbox3), spin_button, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin_button);
+
+
+ GtkWidget *bits_button;
+ bits_button = gtk_check_button_new_with_mnemonic(_("Show network speed in bits"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bits_button),
+ mateconf_client_get_bool(procdata->client,
+ procman::mateconf::network_in_bits.c_str(),
+ NULL));
+ g_signal_connect(G_OBJECT(bits_button), "toggled",
+ G_CALLBACK(network_in_bits_toggled), procdata);
+ gtk_box_pack_start(GTK_BOX(vbox2), bits_button, TRUE, TRUE, 0);
+
+
+
+ hbox2 = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox2, TRUE, TRUE, 0);
+
+ /*
+ * Devices
+ */
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ tab_label = gtk_label_new (_("File Systems"));
+ gtk_widget_show (tab_label);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, tab_label);
+
+ tmp = g_strdup_printf ("<b>%s</b>", _("File Systems"));
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_label_set_markup (GTK_LABEL (label), tmp);
+ g_free (tmp);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
+
+ hbox2 = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0);
+
+ label = gtk_label_new_with_mnemonic (_("_Update interval in seconds:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
+
+ hbox3 = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox2), hbox3, TRUE, TRUE, 0);
+
+ update = (gfloat) procdata->config.disks_update_interval;
+ adjustment = (GtkAdjustment *) gtk_adjustment_new (update / 1000.0, 1.0,
+ 100.0, 1.0, 1.0, 1.0);
+ spin_button = gtk_spin_button_new (adjustment, 1.0, 0);
+ gtk_box_pack_start (GTK_BOX (hbox3), spin_button, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin_button);
+ g_signal_connect (G_OBJECT (spin_button), "focus_out_event",
+ G_CALLBACK(SBU::callback),
+ &disks_interval_updater);
+
+
+ hbox2 = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0);
+ check_button = gtk_check_button_new_with_mnemonic (_("Show _all filesystems"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ procdata->config.show_all_fs);
+ g_signal_connect (G_OBJECT (check_button), "toggled",
+ G_CALLBACK (show_all_fs_toggled), procdata);
+ gtk_box_pack_start (GTK_BOX (hbox2), check_button, FALSE, FALSE, 0);
+
+
+ vbox2 = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
+
+ tmp = g_strdup_printf ("<b>%s</b>", _("Information Fields"));
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_label_set_markup (GTK_LABEL (label), tmp);
+ g_free (tmp);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ vbox3 = create_field_page (procdata->disk_list, _("File system i_nformation shown in list:"));
+ gtk_box_pack_start (GTK_BOX (hbox), vbox3, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (dialog);
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (prefs_dialog_button_pressed), procdata);
+
+ switch (procdata->config.current_tab) {
+ case PROCMAN_TAB_SYSINFO:
+ case PROCMAN_TAB_PROCESSES:
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
+ break;
+ case PROCMAN_TAB_RESOURCES:
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 1);
+ break;
+ case PROCMAN_TAB_DISKS:
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 2);
+ break;
+
+ }
+}
+
+
+
+static char *
+procman_action_to_command(ProcmanActionType type,
+ gint pid,
+ gint extra_value)
+{
+ switch (type) {
+ case PROCMAN_ACTION_KILL:
+ return g_strdup_printf("kill -s %d %d", extra_value, pid);
+ case PROCMAN_ACTION_RENICE:
+ return g_strdup_printf("renice %d %d", extra_value, pid);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+/*
+ * type determines whether if dialog is for killing process or renice.
+ * type == PROCMAN_ACTION_KILL, extra_value -> signal to send
+ * type == PROCMAN_ACTION_RENICE, extra_value -> new priority.
+ */
+gboolean
+procdialog_create_root_password_dialog(ProcmanActionType type,
+ ProcData *procdata,
+ gint pid,
+ gint extra_value)
+{
+ char * command;
+ gboolean ret = FALSE;
+
+ command = procman_action_to_command(type, pid, extra_value);
+
+ procman_debug("Trying to run '%s' as root", command);
+
+ if (procman_has_gksu())
+ ret = procman_gksu_create_root_password_dialog(command);
+ else if (procman_has_matesu())
+ ret = procman_matesu_create_root_password_dialog(command);
+
+ g_free(command);
+ return ret;
+}
+
+
diff --git a/src/procdialogs.h b/src/procdialogs.h
new file mode 100644
index 0000000..eba3212
--- /dev/null
+++ b/src/procdialogs.h
@@ -0,0 +1,54 @@
+/* Procman - dialogs
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#ifndef _PROCDIALOGS_H_
+#define _PROCDIALOGS_H_
+
+
+#include <glib.h>
+#include "procman.h"
+
+/* These are the actual range of settable values. Values outside this range
+ are scaled back to these limits. So show these limits in the slider
+*/
+#ifdef linux
+#define RENICE_VAL_MIN -20
+#define RENICE_VAL_MAX 19
+#else /* ! linux */
+#define RENICE_VAL_MIN -20
+#define RENICE_VAL_MAX 20
+#endif
+
+
+typedef enum
+{
+ PROCMAN_ACTION_RENICE,
+ PROCMAN_ACTION_KILL
+} ProcmanActionType;
+
+
+void procdialog_create_kill_dialog (ProcData *data, int signal);
+void procdialog_create_renice_dialog (ProcData *data);
+gboolean procdialog_create_root_password_dialog (ProcmanActionType type,
+ ProcData *procdata,
+ gint pid, gint extra_value);
+void procdialog_create_memmaps_dialog (ProcData *data);
+void procdialog_create_preferences_dialog (ProcData *data);
+
+#endif
+
diff --git a/src/procman.cpp b/src/procman.cpp
new file mode 100644
index 0000000..16640c3
--- /dev/null
+++ b/src/procman.cpp
@@ -0,0 +1,773 @@
+/* Procman
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <locale.h>
+
+#include <gtkmm/main.h>
+#include <giomm/volumemonitor.h>
+#include <giomm/init.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <bacon-message-connection.h>
+#include <mateconf/mateconf-client.h>
+#include <glibtop.h>
+#include <glibtop/close.h>
+#include <glibtop/loadavg.h>
+
+#include "load-graph.h"
+#include "procman.h"
+#include "interface.h"
+#include "proctable.h"
+#include "prettytable.h"
+#include "callbacks.h"
+#include "smooth_refresh.h"
+#include "util.h"
+#include "mateconf-keys.h"
+#include "argv.h"
+
+
+ProcData::ProcData()
+ : tree(NULL),
+ cpu_graph(NULL),
+ mem_graph(NULL),
+ net_graph(NULL),
+ selected_process(NULL),
+ timeout(0),
+ disk_timeout(0),
+ cpu_total_time(1),
+ cpu_total_time_last(1)
+{ }
+
+
+ProcData* ProcData::get_instance()
+{
+ static ProcData instance;
+ return &instance;
+}
+
+
+static void
+tree_changed_cb (MateConfClient *client, guint id, MateConfEntry *entry, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfValue *value = mateconf_entry_get_value (entry);
+
+ procdata->config.show_tree = mateconf_value_get_bool (value);
+
+ g_object_set(G_OBJECT(procdata->tree),
+ "show-expanders", procdata->config.show_tree,
+ NULL);
+
+ proctable_clear_tree (procdata);
+
+ proctable_update_all (procdata);
+}
+
+static void
+solaris_mode_changed_cb(MateConfClient *client, guint id, MateConfEntry *entry, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfValue *value = mateconf_entry_get_value (entry);
+
+ procdata->config.solaris_mode = mateconf_value_get_bool(value);
+ proctable_update_all (procdata);
+}
+
+
+static void
+network_in_bits_changed_cb(MateConfClient *client, guint id, MateConfEntry *entry, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfValue *value = mateconf_entry_get_value (entry);
+
+ procdata->config.network_in_bits = mateconf_value_get_bool(value);
+ // force scale to be redrawn
+ procdata->net_graph->clear_background();
+}
+
+
+
+static void
+view_as_changed_cb (MateConfClient *client, guint id, MateConfEntry *entry, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ MateConfValue *value = mateconf_entry_get_value (entry);
+
+ procdata->config.whose_process = mateconf_value_get_int (value);
+ procdata->config.whose_process = CLAMP (procdata->config.whose_process, 0, 2);
+ proctable_clear_tree (procdata);
+ proctable_update_all (procdata);
+
+}
+
+static void
+warning_changed_cb (MateConfClient *client, guint id, MateConfEntry *entry, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ const gchar *key = mateconf_entry_get_key (entry);
+ MateConfValue *value = mateconf_entry_get_value (entry);
+
+ if (g_str_equal (key, "/apps/procman/kill_dialog")) {
+ procdata->config.show_kill_warning = mateconf_value_get_bool (value);
+ }
+}
+
+static void
+timeouts_changed_cb (MateConfClient *client, guint id, MateConfEntry *entry, gpointer data)
+{
+ ProcData *procdata = static_cast<ProcData*>(data);
+ const gchar *key = mateconf_entry_get_key (entry);
+ MateConfValue *value = mateconf_entry_get_value (entry);
+
+ if (g_str_equal (key, "/apps/procman/update_interval")) {
+ procdata->config.update_interval = mateconf_value_get_int (value);
+ procdata->config.update_interval =
+ MAX (procdata->config.update_interval, 1000);
+
+ procdata->smooth_refresh->reset();
+
+ if(procdata->timeout) {
+ g_source_remove (procdata->timeout);
+ procdata->timeout = g_timeout_add (procdata->config.update_interval,
+ cb_timeout,
+ procdata);
+ }
+ }
+ else if (g_str_equal (key, "/apps/procman/graph_update_interval")){
+ procdata->config.graph_update_interval = mateconf_value_get_int (value);
+ procdata->config.graph_update_interval =
+ MAX (procdata->config.graph_update_interval,
+ 250);
+ load_graph_change_speed(procdata->cpu_graph,
+ procdata->config.graph_update_interval);
+ load_graph_change_speed(procdata->mem_graph,
+ procdata->config.graph_update_interval);
+ load_graph_change_speed(procdata->net_graph,
+ procdata->config.graph_update_interval);
+ }
+ else if (g_str_equal(key, "/apps/procman/disks_interval")) {
+
+ procdata->config.disks_update_interval = mateconf_value_get_int (value);
+ procdata->config.disks_update_interval =
+ MAX (procdata->config.disks_update_interval, 1000);
+
+ if(procdata->disk_timeout) {
+ g_source_remove (procdata->disk_timeout);
+ procdata->disk_timeout = \
+ g_timeout_add (procdata->config.disks_update_interval,
+ cb_update_disks,
+ procdata);
+ }
+ }
+ else {
+ g_assert_not_reached();
+ }
+}
+
+static void
+color_changed_cb (MateConfClient *client, guint id, MateConfEntry *entry, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ const gchar *key = mateconf_entry_get_key (entry);
+ MateConfValue *value = mateconf_entry_get_value (entry);
+ const gchar *color = mateconf_value_get_string (value);
+
+ if (g_str_has_prefix (key, "/apps/procman/cpu_color")) {
+ for (int i = 0; i < GLIBTOP_NCPU; i++) {
+ string cpu_key = make_string(g_strdup_printf("/apps/procman/cpu_color%d", i));
+ if (cpu_key == key) {
+ gdk_color_parse (color, &procdata->config.cpu_color[i]);
+ procdata->cpu_graph->colors.at(i) = procdata->config.cpu_color[i];
+ break;
+ }
+ }
+ }
+ else if (g_str_equal (key, "/apps/procman/mem_color")) {
+ gdk_color_parse (color, &procdata->config.mem_color);
+ procdata->mem_graph->colors.at(0) = procdata->config.mem_color;
+ }
+ else if (g_str_equal (key, "/apps/procman/swap_color")) {
+ gdk_color_parse (color, &procdata->config.swap_color);
+ procdata->mem_graph->colors.at(1) = procdata->config.swap_color;
+ }
+ else if (g_str_equal (key, "/apps/procman/net_in_color")) {
+ gdk_color_parse (color, &procdata->config.net_in_color);
+ procdata->net_graph->colors.at(0) = procdata->config.net_in_color;
+ }
+ else if (g_str_equal (key, "/apps/procman/net_out_color")) {
+ gdk_color_parse (color, &procdata->config.net_out_color);
+ procdata->net_graph->colors.at(1) = procdata->config.net_out_color;
+ }
+ else {
+ g_assert_not_reached();
+ }
+}
+
+
+
+static void
+show_all_fs_changed_cb (MateConfClient *client, guint id, MateConfEntry *entry, gpointer data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(data);
+ MateConfValue *value = mateconf_entry_get_value (entry);
+
+ procdata->config.show_all_fs = mateconf_value_get_bool (value);
+
+ cb_update_disks (data);
+}
+
+
+static ProcData *
+procman_data_new (MateConfClient *client)
+{
+
+ ProcData *pd;
+ gchar *color;
+ gint swidth, sheight;
+ gint i;
+ glibtop_cpu cpu;
+
+ pd = ProcData::get_instance();
+
+ pd->config.width = mateconf_client_get_int (client, "/apps/procman/width", NULL);
+ pd->config.height = mateconf_client_get_int (client, "/apps/procman/height", NULL);
+ pd->config.show_tree = mateconf_client_get_bool (client, "/apps/procman/show_tree", NULL);
+ mateconf_client_notify_add (client, "/apps/procman/show_tree", tree_changed_cb,
+ pd, NULL, NULL);
+
+ pd->config.solaris_mode = mateconf_client_get_bool(client, procman::mateconf::solaris_mode.c_str(), NULL);
+ mateconf_client_notify_add(client, procman::mateconf::solaris_mode.c_str(), solaris_mode_changed_cb, pd, NULL, NULL);
+
+ pd->config.network_in_bits = mateconf_client_get_bool(client, procman::mateconf::network_in_bits.c_str(), NULL);
+ mateconf_client_notify_add(client, procman::mateconf::network_in_bits.c_str(), network_in_bits_changed_cb, pd, NULL, NULL);
+
+
+ pd->config.show_kill_warning = mateconf_client_get_bool (client, "/apps/procman/kill_dialog",
+ NULL);
+ mateconf_client_notify_add (client, "/apps/procman/kill_dialog", warning_changed_cb,
+ pd, NULL, NULL);
+ pd->config.update_interval = mateconf_client_get_int (client, "/apps/procman/update_interval",
+ NULL);
+ mateconf_client_notify_add (client, "/apps/procman/update_interval", timeouts_changed_cb,
+ pd, NULL, NULL);
+ pd->config.graph_update_interval = mateconf_client_get_int (client,
+ "/apps/procman/graph_update_interval",
+ NULL);
+ mateconf_client_notify_add (client, "/apps/procman/graph_update_interval", timeouts_changed_cb,
+ pd, NULL, NULL);
+ pd->config.disks_update_interval = mateconf_client_get_int (client,
+ "/apps/procman/disks_interval",
+ NULL);
+ mateconf_client_notify_add (client, "/apps/procman/disks_interval", timeouts_changed_cb,
+ pd, NULL, NULL);
+
+
+ /* /apps/procman/show_all_fs */
+ pd->config.show_all_fs = mateconf_client_get_bool (
+ client, "/apps/procman/show_all_fs",
+ NULL);
+ mateconf_client_notify_add
+ (client, "/apps/procman/show_all_fs",
+ show_all_fs_changed_cb, pd, NULL, NULL);
+
+
+ pd->config.whose_process = mateconf_client_get_int (client, "/apps/procman/view_as", NULL);
+ mateconf_client_notify_add (client, "/apps/procman/view_as", view_as_changed_cb,
+ pd, NULL, NULL);
+ pd->config.current_tab = mateconf_client_get_int (client, "/apps/procman/current_tab", NULL);
+
+ for (int i = 0; i < GLIBTOP_NCPU; i++) {
+ gchar *key;
+ key = g_strdup_printf ("/apps/procman/cpu_color%d", i);
+
+ color = mateconf_client_get_string (client, key, NULL);
+ if (!color)
+ color = g_strdup ("#f25915e815e8");
+ mateconf_client_notify_add (client, key,
+ color_changed_cb, pd, NULL, NULL);
+ gdk_color_parse(color, &pd->config.cpu_color[i]);
+ g_free (color);
+ g_free (key);
+ }
+ color = mateconf_client_get_string (client, "/apps/procman/mem_color", NULL);
+ if (!color)
+ color = g_strdup ("#000000ff0082");
+ mateconf_client_notify_add (client, "/apps/procman/mem_color",
+ color_changed_cb, pd, NULL, NULL);
+ gdk_color_parse(color, &pd->config.mem_color);
+
+ g_free (color);
+
+ color = mateconf_client_get_string (client, "/apps/procman/swap_color", NULL);
+ if (!color)
+ color = g_strdup ("#00b6000000ff");
+ mateconf_client_notify_add (client, "/apps/procman/swap_color",
+ color_changed_cb, pd, NULL, NULL);
+ gdk_color_parse(color, &pd->config.swap_color);
+ g_free (color);
+
+ color = mateconf_client_get_string (client, "/apps/procman/net_in_color", NULL);
+ if (!color)
+ color = g_strdup ("#000000f200f2");
+ mateconf_client_notify_add (client, "/apps/procman/net_in_color",
+ color_changed_cb, pd, NULL, NULL);
+ gdk_color_parse(color, &pd->config.net_in_color);
+ g_free (color);
+
+ color = mateconf_client_get_string (client, "/apps/procman/net_out_color", NULL);
+ if (!color)
+ color = g_strdup ("#00f2000000c1");
+ mateconf_client_notify_add (client, "/apps/procman/net_out_color",
+ color_changed_cb, pd, NULL, NULL);
+ gdk_color_parse(color, &pd->config.net_out_color);
+ g_free (color);
+
+ /* Sanity checks */
+ swidth = gdk_screen_width ();
+ sheight = gdk_screen_height ();
+ pd->config.width = CLAMP (pd->config.width, 50, swidth);
+ pd->config.height = CLAMP (pd->config.height, 50, sheight);
+ pd->config.update_interval = MAX (pd->config.update_interval, 1000);
+ pd->config.graph_update_interval = MAX (pd->config.graph_update_interval, 250);
+ pd->config.disks_update_interval = MAX (pd->config.disks_update_interval, 1000);
+ pd->config.whose_process = CLAMP (pd->config.whose_process, 0, 2);
+ pd->config.current_tab = CLAMP(pd->config.current_tab,
+ PROCMAN_TAB_SYSINFO,
+ PROCMAN_TAB_DISKS);
+
+ /* Determinie number of cpus since libgtop doesn't really tell you*/
+ pd->config.num_cpus = 0;
+ glibtop_get_cpu (&cpu);
+ pd->frequency = cpu.frequency;
+ i=0;
+ while (i < GLIBTOP_NCPU && cpu.xcpu_total[i] != 0) {
+ pd->config.num_cpus ++;
+ i++;
+ }
+ if (pd->config.num_cpus == 0)
+ pd->config.num_cpus = 1;
+
+ // delayed initialization as SmoothRefresh() needs ProcData
+ // i.e. we can't call ProcData::get_instance
+ pd->smooth_refresh = new SmoothRefresh();
+
+ return pd;
+
+}
+
+static void
+procman_free_data (ProcData *procdata)
+{
+
+ proctable_free_table (procdata);
+ delete procdata->smooth_refresh;
+}
+
+
+gboolean
+procman_get_tree_state (MateConfClient *client, GtkWidget *tree, const gchar *prefix)
+{
+ GtkTreeModel *model;
+ GList *columns, *it;
+ gint sort_col;
+ GtkSortType order;
+ gchar *key;
+
+
+ g_assert(tree);
+ g_assert(prefix);
+
+ if (!mateconf_client_dir_exists (client, prefix, NULL))
+ return FALSE;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+
+ key = g_strdup_printf ("%s/sort_col", prefix);
+ sort_col = mateconf_client_get_int (client, key, NULL);
+ g_free (key);
+
+ key = g_strdup_printf ("%s/sort_order", prefix);
+ order = static_cast<GtkSortType>(mateconf_client_get_int (client, key, NULL));
+ g_free (key);
+
+ if (sort_col != -1)
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
+ sort_col,
+ order);
+
+ columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (tree));
+
+ for(it = columns; it; it = it->next)
+ {
+ GtkTreeViewColumn *column;
+ MateConfValue *value = NULL;
+ gint width;
+ gboolean visible;
+ int id;
+
+ column = static_cast<GtkTreeViewColumn*>(it->data);
+ id = gtk_tree_view_column_get_sort_column_id (column);
+
+ key = g_strdup_printf ("%s/col_%d_width", prefix, id);
+ value = mateconf_client_get (client, key, NULL);
+ g_free (key);
+
+ if (value != NULL) {
+ width = mateconf_value_get_int(value);
+ mateconf_value_free (value);
+
+ key = g_strdup_printf ("%s/col_%d_visible", prefix, id);
+ visible = mateconf_client_get_bool (client, key, NULL);
+ g_free (key);
+
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree), id);
+ if(!column) continue;
+ gtk_tree_view_column_set_visible (column, visible);
+ if (visible) {
+ /* ensure column is really visible */
+ width = MAX(width, 10);
+ gtk_tree_view_column_set_fixed_width(column, width);
+ }
+ }
+ }
+
+ if(g_str_has_suffix(prefix, "proctree") || g_str_has_suffix(prefix, "disktreenew"))
+ {
+ GSList *order;
+ char *key;
+
+ key = g_strdup_printf("%s/columns_order", prefix);
+ order = mateconf_client_get_list(client, key, MATECONF_VALUE_INT, NULL);
+ proctable_set_columns_order(GTK_TREE_VIEW(tree), order);
+
+ g_slist_free(order);
+ g_free(key);
+ }
+
+ g_list_free(columns);
+
+ return TRUE;
+}
+
+void
+procman_save_tree_state (MateConfClient *client, GtkWidget *tree, const gchar *prefix)
+{
+ GtkTreeModel *model;
+ GList *it, *columns;
+ gint sort_col;
+ GtkSortType order;
+
+ g_assert(tree);
+ g_assert(prefix);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+ if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), &sort_col,
+ &order)) {
+ gchar *key;
+
+ key = g_strdup_printf ("%s/sort_col", prefix);
+ mateconf_client_set_int (client, key, sort_col, NULL);
+ g_free (key);
+
+ key = g_strdup_printf ("%s/sort_order", prefix);
+ mateconf_client_set_int (client, key, order, NULL);
+ g_free (key);
+ }
+
+ columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (tree));
+
+ for(it = columns; it; it = it->next)
+ {
+ GtkTreeViewColumn *column;
+ gboolean visible;
+ gint width;
+ gchar *key;
+ int id;
+
+ column = static_cast<GtkTreeViewColumn*>(it->data);
+ id = gtk_tree_view_column_get_sort_column_id (column);
+ visible = gtk_tree_view_column_get_visible (column);
+ width = gtk_tree_view_column_get_width (column);
+
+ key = g_strdup_printf ("%s/col_%d_width", prefix, id);
+ mateconf_client_set_int (client, key, width, NULL);
+ g_free (key);
+
+ key = g_strdup_printf ("%s/col_%d_visible", prefix, id);
+ mateconf_client_set_bool (client, key, visible, NULL);
+ g_free (key);
+ }
+
+ if(g_str_has_suffix(prefix, "proctree") || g_str_has_suffix(prefix, "disktreenew"))
+ {
+ GSList *order;
+ char *key;
+ GError *error = NULL;
+
+ key = g_strdup_printf("%s/columns_order", prefix);
+ order = proctable_get_columns_order(GTK_TREE_VIEW(tree));
+
+ if(!mateconf_client_set_list(client, key, MATECONF_VALUE_INT, order, &error))
+ {
+ g_critical("Could not save MateConf key '%s' : %s",
+ key,
+ error->message);
+ g_error_free(error);
+ }
+
+ g_slist_free(order);
+ g_free(key);
+ }
+
+ g_list_free(columns);
+}
+
+void
+procman_save_config (ProcData *data)
+{
+ MateConfClient *client = data->client;
+
+
+ g_assert(data);
+
+ procman_save_tree_state (data->client, data->tree, "/apps/procman/proctree");
+ procman_save_tree_state (data->client, data->disk_list, "/apps/procman/disktreenew");
+
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ data->config.width = gdk_window_get_width(gtk_widget_get_window(data->app));
+ data->config.height = gdk_window_get_height(gtk_widget_get_window(data->app));
+ #else
+ gint width, height;
+
+ gdk_drawable_get_size(gtk_widget_get_window(data->app), &width, &height);
+
+ data->config.width = width;
+ data->config.height = height;
+ #endif
+
+ mateconf_client_set_int (client, "/apps/procman/width", data->config.width, NULL);
+ mateconf_client_set_int (client, "/apps/procman/height", data->config.height, NULL);
+ mateconf_client_set_int (client, "/apps/procman/current_tab", data->config.current_tab, NULL);
+
+ mateconf_client_suggest_sync (client, NULL);
+
+
+
+}
+
+static guint32
+get_startup_timestamp ()
+{
+ const gchar *startup_id_env;
+ gchar *startup_id = NULL;
+ gchar *time_str;
+ gulong retval = 0;
+
+ /* we don't unset the env, since startup-notification
+ * may still need it */
+ startup_id_env = g_getenv ("DESKTOP_STARTUP_ID");
+ if (startup_id_env == NULL)
+ goto out;
+
+ startup_id = g_strdup (startup_id_env);
+
+ time_str = g_strrstr (startup_id, "_TIME");
+ if (time_str == NULL)
+ goto out;
+
+ /* Skip past the "_TIME" part */
+ time_str += 5;
+
+ retval = strtoul (time_str, NULL, 0);
+
+ out:
+ g_free (startup_id);
+
+ return retval;
+}
+
+
+static void
+cb_server (const gchar *msg, gpointer user_data)
+{
+ GdkWindow *window;
+ ProcData *procdata;
+ guint32 timestamp = 0;
+
+ window = gdk_get_default_root_window ();
+
+ procdata = *(ProcData**)user_data;
+ g_assert (procdata != NULL);
+
+ procman_debug("cb_server(%s)", msg);
+ if (msg != NULL && procman::SHOW_SYSTEM_TAB_CMD == msg) {
+ procman_debug("Changing to PROCMAN_TAB_SYSINFO via bacon message");
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_SYSINFO);
+ cb_change_current_page(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_SYSINFO, procdata);
+ } else
+ timestamp = strtoul(msg, NULL, 0);
+
+ if (timestamp == 0)
+ {
+ /* fall back to rountripping to X */
+ timestamp = gdk_x11_get_server_time (window);
+ }
+
+ gdk_x11_window_set_user_time (window, timestamp);
+
+ gtk_window_present (GTK_WINDOW(procdata->app));
+}
+
+
+
+
+static void
+mount_changed(const Glib::RefPtr<Gio::Mount>&)
+{
+ cb_update_disks(ProcData::get_instance());
+}
+
+
+static void
+init_volume_monitor(ProcData *procdata)
+{
+ using namespace Gio;
+ using namespace Glib;
+
+ RefPtr<VolumeMonitor> monitor = VolumeMonitor::get();
+
+ monitor->signal_mount_added().connect(sigc::ptr_fun(&mount_changed));
+ monitor->signal_mount_changed().connect(sigc::ptr_fun(&mount_changed));
+ monitor->signal_mount_removed().connect(sigc::ptr_fun(&mount_changed));
+}
+
+
+namespace procman
+{
+ const std::string SHOW_SYSTEM_TAB_CMD("SHOWSYSTAB");
+}
+
+
+
+int
+main (int argc, char *argv[])
+{
+ guint32 startup_timestamp;
+ MateConfClient *client;
+ ProcData *procdata;
+ BaconMessageConnection *conn;
+
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+ setlocale (LC_ALL, "");
+
+ startup_timestamp = get_startup_timestamp();
+
+ Glib::OptionContext context;
+ context.set_summary(_("A simple process and system monitor."));
+ context.set_ignore_unknown_options(true);
+ procman::OptionGroup option_group;
+ context.set_main_group(option_group);
+
+ try {
+ context.parse(argc, argv);
+ } catch (const Glib::Error& ex) {
+ g_error("Arguments parse error : %s", ex.what().c_str());
+ }
+
+ Gio::init();
+ Gtk::Main kit(&argc, &argv);
+ procman_debug("post gtk_init");
+
+ conn = bacon_message_connection_new ("mate-system-monitor");
+ if (!conn) g_error("Couldn't connect to mate-system-monitor");
+
+ if (bacon_message_connection_get_is_server (conn))
+ {
+ bacon_message_connection_set_callback (conn, cb_server, &procdata);
+ }
+ else /* client */
+ {
+ char *timestamp;
+
+ timestamp = g_strdup_printf ("%" G_GUINT32_FORMAT, startup_timestamp);
+
+ if (option_group.show_system_tab)
+ bacon_message_connection_send(conn, procman::SHOW_SYSTEM_TAB_CMD.c_str());
+
+ bacon_message_connection_send (conn, timestamp);
+
+ gdk_notify_startup_complete ();
+
+ g_free (timestamp);
+ bacon_message_connection_free (conn);
+
+ exit (0);
+ }
+
+ gtk_window_set_default_icon_name ("utilities-system-monitor");
+ g_set_application_name(_("System Monitor"));
+
+ mateconf_init (argc, argv, NULL);
+
+ client = mateconf_client_get_default ();
+ mateconf_client_add_dir(client, "/apps/procman", MATECONF_CLIENT_PRELOAD_NONE, NULL);
+
+ glibtop_init ();
+
+ procman_debug("end init");
+
+ procdata = procman_data_new (client);
+ procdata->client = client;
+
+ procman_debug("begin create_main_window");
+ create_main_window (procdata);
+ procman_debug("end create_main_window");
+
+ // proctable_update_all (procdata);
+
+ init_volume_monitor (procdata);
+
+ g_assert(procdata->app);
+
+ if (option_group.show_system_tab) {
+ procman_debug("Starting with PROCMAN_TAB_SYSINFO by commandline request");
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_SYSINFO);
+ cb_change_current_page (GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_SYSINFO, procdata);
+ }
+
+ gtk_widget_show(procdata->app);
+
+ procman_debug("begin gtk_main");
+ kit.run();
+
+ procman_free_data (procdata);
+
+ glibtop_close ();
+
+ return 0;
+}
+
diff --git a/src/procman.h b/src/procman.h
new file mode 100644
index 0000000..76c998f
--- /dev/null
+++ b/src/procman.h
@@ -0,0 +1,247 @@
+/* Procman
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#ifndef _PROCMAN_PROCMAN_H_
+#define _PROCMAN_PROCMAN_H_
+
+
+#include <glibmm/refptr.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+#include <glibtop/cpu.h>
+
+#include <time.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+
+struct ProcInfo;
+struct ProcData;
+struct LoadGraph;
+
+#include "smooth_refresh.h"
+#include "prettytable.h"
+
+enum
+{
+ ALL_PROCESSES,
+ MY_PROCESSES,
+ ACTIVE_PROCESSES
+};
+
+
+static const unsigned MIN_UPDATE_INTERVAL = 1 * 1000;
+static const unsigned MAX_UPDATE_INTERVAL = 100 * 1000;
+
+
+namespace procman
+{
+ extern const std::string SHOW_SYSTEM_TAB_CMD;
+}
+
+
+
+enum ProcmanTab
+{
+ PROCMAN_TAB_SYSINFO,
+ PROCMAN_TAB_PROCESSES,
+ PROCMAN_TAB_RESOURCES,
+ PROCMAN_TAB_DISKS
+};
+
+
+struct ProcConfig
+{
+ gint width;
+ gint height;
+ gboolean show_kill_warning;
+ gboolean show_tree;
+ gboolean show_all_fs;
+ int update_interval;
+ int graph_update_interval;
+ int disks_update_interval;
+ gint whose_process;
+ gint current_tab;
+ GdkColor cpu_color[GLIBTOP_NCPU];
+ GdkColor mem_color;
+ GdkColor swap_color;
+ GdkColor net_in_color;
+ GdkColor net_out_color;
+ GdkColor bg_color;
+ GdkColor frame_color;
+ gint num_cpus;
+ bool solaris_mode;
+ bool network_in_bits;
+};
+
+
+
+struct MutableProcInfo
+{
+ MutableProcInfo()
+ : status(0)
+ { }
+
+ std::string user;
+
+ gchar wchan[40];
+
+ // all these members are filled with libgtop which uses
+ // guint64 (to have fixed size data) but we don't need more
+ // than an unsigned long (even for 32bit apps on a 64bit
+ // kernel) as these data are amounts, not offsets.
+ gulong vmsize;
+ gulong memres;
+ gulong memshared;
+ gulong memwritable;
+ gulong mem;
+
+ // wnck gives an unsigned long
+ gulong memxserver;
+
+ gulong start_time;
+ guint64 cpu_time;
+ guint status;
+ guint pcpu;
+ gint nice;
+};
+
+
+class ProcInfo
+ : public MutableProcInfo
+{
+ /* undefined */ ProcInfo& operator=(const ProcInfo&);
+ /* undefined */ ProcInfo(const ProcInfo&);
+
+ typedef std::map<guint, std::string> UserMap;
+ /* cached username */
+ static UserMap users;
+
+ public:
+
+ // TODO: use a set instead
+ // sorted by pid. The map has a nice property : it is sorted
+ // by pid so this helps a lot when looking for the parent node
+ // as ppid is nearly always < pid.
+ typedef std::map<pid_t, ProcInfo*> List;
+ typedef List::iterator Iterator;
+
+ static List all;
+
+ static ProcInfo* find(pid_t pid);
+ static Iterator begin() { return ProcInfo::all.begin(); }
+ static Iterator end() { return ProcInfo::all.end(); }
+
+
+ ProcInfo(pid_t pid);
+ ~ProcInfo();
+ // adds one more ref to icon
+ void set_icon(Glib::RefPtr<Gdk::Pixbuf> icon);
+ void set_user(guint uid);
+
+ GtkTreeIter node;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf;
+ gchar *tooltip;
+ gchar *name;
+ gchar *arguments;
+
+ gchar *security_context;
+
+ const guint pid;
+ guint ppid;
+ guint uid;
+
+// private:
+ // tracks cpu time per process keeps growing because if a
+ // ProcInfo is deleted this does not mean that the process is
+ // not going to be recreated on the next update. For example,
+ // if dependencies + (My or Active), the proclist is cleared
+ // on each update. This is a workaround
+ static std::map<pid_t, guint64> cpu_times;
+};
+
+struct ProcData
+{
+ // lazy initialization
+ static ProcData* get_instance();
+
+ GtkUIManager *uimanager;
+ GtkActionGroup *action_group;
+ GtkWidget *statusbar;
+ gint tip_message_cid;
+ GtkWidget *tree;
+ GtkWidget *loadavg;
+ GtkWidget *endprocessbutton;
+ GtkWidget *popup_menu;
+ GtkWidget *disk_list;
+ GtkWidget *notebook;
+ ProcConfig config;
+ LoadGraph *cpu_graph;
+ LoadGraph *mem_graph;
+ LoadGraph *net_graph;
+ gint cpu_label_fixed_width;
+ gint net_label_fixed_width;
+ ProcInfo *selected_process;
+ GtkTreeSelection *selection;
+ guint timeout;
+ guint disk_timeout;
+
+ PrettyTable pretty_table;
+
+ MateConfClient *client;
+ GtkWidget *app;
+ GtkUIManager *menu;
+
+ unsigned frequency;
+
+ SmoothRefresh *smooth_refresh;
+
+ guint64 cpu_total_time;
+ guint64 cpu_total_time_last;
+
+private:
+ ProcData();
+ /* undefined */ ProcData(const ProcData &);
+ /* undefined */ ProcData& operator=(const ProcData &);
+};
+
+void procman_save_config (ProcData *data);
+void procman_save_tree_state (MateConfClient *client, GtkWidget *tree, const gchar *prefix);
+gboolean procman_get_tree_state (MateConfClient *client, GtkWidget *tree, const gchar *prefix);
+
+
+
+
+
+struct ReniceArgs
+{
+ ProcData *procdata;
+ int nice_value;
+};
+
+
+struct KillArgs
+{
+ ProcData *procdata;
+ int signal;
+};
+
+#endif /* _PROCMAN_PROCMAN_H_ */
diff --git a/src/procman_gksu.cpp b/src/procman_gksu.cpp
new file mode 100644
index 0000000..d2737cc
--- /dev/null
+++ b/src/procman_gksu.cpp
@@ -0,0 +1,54 @@
+#include <config.h>
+
+#include "procman.h"
+#include "procman_gksu.h"
+#include "util.h"
+
+static gboolean (*gksu_run)(const char *, GError **);
+
+
+static void load_gksu(void)
+{
+ static gboolean init;
+
+ if (init)
+ return;
+
+ init = TRUE;
+
+ load_symbols("libgksu2.so",
+ "gksu_run", &gksu_run,
+ NULL);
+}
+
+
+
+
+
+gboolean procman_gksu_create_root_password_dialog(const char *command)
+{
+ GError *e = NULL;
+
+ /* Returns FALSE or TRUE on success, depends on version ... */
+ gksu_run(command, &e);
+
+ if (e) {
+ g_critical("Could not run gksu_run(\"%s\") : %s\n",
+ command, e->message);
+ g_error_free(e);
+ return FALSE;
+ }
+
+ g_message("gksu_run did fine\n");
+ return TRUE;
+}
+
+
+
+gboolean
+procman_has_gksu(void)
+{
+ load_gksu();
+ return gksu_run != NULL;
+}
+
diff --git a/src/procman_gksu.h b/src/procman_gksu.h
new file mode 100644
index 0000000..505220e
--- /dev/null
+++ b/src/procman_gksu.h
@@ -0,0 +1,12 @@
+#ifndef H_MATE_SYSTEM_MONITOR_GKSU_H_1132171928
+#define H_MATE_SYSTEM_MONITOR_GKSU_H_1132171928
+
+#include <glib.h>
+
+gboolean
+procman_gksu_create_root_password_dialog(const char * command);
+
+gboolean
+procman_has_gksu(void) G_GNUC_CONST;
+
+#endif /* H_MATE_SYSTEM_MONITOR_GKSU_H_1132171928 */
diff --git a/src/procman_matesu.cpp b/src/procman_matesu.cpp
new file mode 100644
index 0000000..5bf6c59
--- /dev/null
+++ b/src/procman_matesu.cpp
@@ -0,0 +1,40 @@
+#include <config.h>
+
+#include <glib.h>
+
+#include "procman.h"
+#include "procman_matesu.h"
+#include "util.h"
+
+gboolean (*matesu_exec)(const char *commandline);
+
+
+static void
+load_matesu(void)
+{
+ static gboolean init;
+
+ if (init)
+ return;
+
+ init = TRUE;
+
+ load_symbols("libmatesu.so.0",
+ "matesu_exec", &matesu_exec,
+ NULL);
+}
+
+
+gboolean
+procman_matesu_create_root_password_dialog(const char *command)
+{
+ return matesu_exec(command);
+}
+
+
+gboolean
+procman_has_matesu(void)
+{
+ load_matesu();
+ return matesu_exec != NULL;
+}
diff --git a/src/procman_matesu.h b/src/procman_matesu.h
new file mode 100644
index 0000000..099c756
--- /dev/null
+++ b/src/procman_matesu.h
@@ -0,0 +1,12 @@
+#ifndef H_MATE_SYSTEM_MONITOR_MATESU_H_1132171917
+#define H_MATE_SYSTEM_MONITOR_MATESU_H_1132171917
+
+#include <glib/gtypes.h>
+
+gboolean
+procman_matesu_create_root_password_dialog(const char * message);
+
+gboolean
+procman_has_matesu(void) G_GNUC_CONST;
+
+#endif /* H_MATE_SYSTEM_MONITOR_MATESU_H_1132171917 */
diff --git a/src/proctable.cpp b/src/proctable.cpp
new file mode 100644
index 0000000..7f192b2
--- /dev/null
+++ b/src/proctable.cpp
@@ -0,0 +1,964 @@
+/* Procman tree view and process updating
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <config.h>
+
+
+#include <string.h>
+#include <math.h>
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glibtop.h>
+#include <glibtop/loadavg.h>
+#include <glibtop/proclist.h>
+#include <glibtop/procstate.h>
+#include <glibtop/procmem.h>
+#include <glibtop/procmap.h>
+#include <glibtop/proctime.h>
+#include <glibtop/procuid.h>
+#include <glibtop/procargs.h>
+#include <glibtop/prockernel.h>
+#include <glibtop/mem.h>
+#include <glibtop/swap.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <time.h>
+
+#include <set>
+#include <list>
+
+#include "procman.h"
+#include "selection.h"
+#include "proctable.h"
+#include "callbacks.h"
+#include "prettytable.h"
+#include "util.h"
+#include "interface.h"
+#include "selinux.h"
+
+
+ProcInfo::UserMap ProcInfo::users;
+ProcInfo::List ProcInfo::all;
+std::map<pid_t, guint64> ProcInfo::cpu_times;
+
+
+ProcInfo* ProcInfo::find(pid_t pid)
+{
+ Iterator it(ProcInfo::all.find(pid));
+ return (it == ProcInfo::all.end() ? NULL : it->second);
+}
+
+
+
+
+static void
+set_proctree_reorderable(ProcData *procdata)
+{
+ GList *columns, *col;
+ GtkTreeView *proctree;
+
+ proctree = GTK_TREE_VIEW(procdata->tree);
+
+ columns = gtk_tree_view_get_columns (proctree);
+
+ for(col = columns; col; col = col->next)
+ gtk_tree_view_column_set_reorderable(static_cast<GtkTreeViewColumn*>(col->data), TRUE);
+
+ g_list_free(columns);
+}
+
+
+static void
+cb_columns_changed(GtkTreeView *treeview, gpointer user_data)
+{
+ ProcData * const procdata = static_cast<ProcData*>(user_data);
+
+ procman_save_tree_state(procdata->client,
+ GTK_WIDGET(treeview),
+ "/apps/procman/proctree");
+}
+
+
+static GtkTreeViewColumn*
+my_gtk_tree_view_get_column_with_sort_column_id(GtkTreeView *treeview, int id)
+{
+ GList *columns, *it;
+ GtkTreeViewColumn *col = NULL;
+
+ columns = gtk_tree_view_get_columns(treeview);
+
+ for(it = columns; it; it = it->next)
+ {
+ if(gtk_tree_view_column_get_sort_column_id(static_cast<GtkTreeViewColumn*>(it->data)) == id)
+ {
+ col = static_cast<GtkTreeViewColumn*>(it->data);
+ break;
+ }
+ }
+
+ g_list_free(columns);
+
+ return col;
+}
+
+
+void
+proctable_set_columns_order(GtkTreeView *treeview, GSList *order)
+{
+ GtkTreeViewColumn* last = NULL;
+ GSList *it;
+
+ for(it = order; it; it = it->next)
+ {
+ int id;
+ GtkTreeViewColumn *cur;
+
+ id = GPOINTER_TO_INT(it->data);
+
+ g_assert(id >= 0 && id < NUM_COLUMNS);
+
+ cur = my_gtk_tree_view_get_column_with_sort_column_id(treeview, id);
+
+ if(cur && cur != last)
+ {
+ gtk_tree_view_move_column_after(treeview, cur, last);
+ last = cur;
+ }
+ }
+}
+
+
+
+GSList*
+proctable_get_columns_order(GtkTreeView *treeview)
+{
+ GList *columns, *col;
+ GSList *order = NULL;
+
+ columns = gtk_tree_view_get_columns(treeview);
+
+ for(col = columns; col; col = col->next)
+ {
+ int id;
+
+ id = gtk_tree_view_column_get_sort_column_id(static_cast<GtkTreeViewColumn*>(col->data));
+ order = g_slist_prepend(order, GINT_TO_POINTER(id));
+ }
+
+ g_list_free(columns);
+
+ order = g_slist_reverse(order);
+
+ return order;
+}
+
+
+static gboolean
+search_equal_func(GtkTreeModel *model,
+ gint column,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer search_data)
+{
+ char* name;
+ char* user;
+ gboolean found;
+
+ gtk_tree_model_get(model, iter,
+ COL_NAME, &name,
+ COL_USER, &user,
+ -1);
+
+ found = !((name && strstr(name, key))
+ || (user && strstr(user, key)));
+
+ g_free(name);
+ g_free(user);
+
+ return found;
+}
+
+
+
+GtkWidget *
+proctable_new (ProcData * const procdata)
+{
+ GtkWidget *proctree;
+ GtkWidget *scrolled;
+ GtkTreeStore *model;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell_renderer;
+
+ const gchar *titles[] = {
+ N_("Process Name"),
+ N_("User"),
+ N_("Status"),
+ N_("Virtual Memory"),
+ N_("Resident Memory"),
+ N_("Writable Memory"),
+ N_("Shared Memory"),
+ N_("X Server Memory"),
+ /* xgettext:no-c-format */ N_("% CPU"),
+ N_("CPU Time"),
+ N_("Started"),
+ N_("Nice"),
+ N_("ID"),
+ N_("Security Context"),
+ N_("Command Line"),
+ N_("Memory"),
+ /* xgettext: wchan, see ps(1) or top(1) */
+ N_("Waiting Channel"),
+ NULL,
+ "POINTER"
+ };
+
+ gint i;
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ model = gtk_tree_store_new (NUM_COLUMNS,
+ G_TYPE_STRING, /* Process Name */
+ G_TYPE_STRING, /* User */
+ G_TYPE_UINT, /* Status */
+ G_TYPE_ULONG, /* VM Size */
+ G_TYPE_ULONG, /* Resident Memory */
+ G_TYPE_ULONG, /* Writable Memory */
+ G_TYPE_ULONG, /* Shared Memory */
+ G_TYPE_ULONG, /* X Server Memory */
+ G_TYPE_UINT, /* % CPU */
+ G_TYPE_UINT64, /* CPU time */
+ G_TYPE_ULONG, /* Started */
+ G_TYPE_INT, /* Nice */
+ G_TYPE_UINT, /* ID */
+ G_TYPE_STRING, /* Security Context */
+ G_TYPE_STRING, /* Arguments */
+ G_TYPE_ULONG, /* Memory */
+ G_TYPE_STRING, /* wchan */
+ GDK_TYPE_PIXBUF, /* Icon */
+ G_TYPE_POINTER, /* ProcInfo */
+ G_TYPE_STRING /* Sexy tooltip */
+ );
+
+ proctree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+ gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (proctree), COL_TOOLTIP);
+ g_object_set(G_OBJECT(proctree),
+ "show-expanders", procdata->config.show_tree,
+ NULL);
+ gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (proctree),
+ search_equal_func,
+ NULL,
+ NULL);
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (proctree), TRUE);
+ g_object_unref (G_OBJECT (model));
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (proctree));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+ column = gtk_tree_view_column_new ();
+
+ cell_renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell_renderer,
+ "pixbuf", COL_PIXBUF,
+ NULL);
+
+ cell_renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell_renderer,
+ "text", COL_NAME,
+ NULL);
+ gtk_tree_view_column_set_title (column, _(titles[0]));
+ gtk_tree_view_column_set_sort_column_id (column, COL_NAME);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width (column, 1);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (proctree), column);
+ gtk_tree_view_set_expander_column (GTK_TREE_VIEW (proctree), column);
+
+
+ for (i = COL_USER; i <= COL_WCHAN; i++) {
+
+ GtkCellRenderer *cell;
+ GtkTreeViewColumn *col;
+
+ cell = gtk_cell_renderer_text_new();
+ col = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(col, cell, TRUE);
+ gtk_tree_view_column_set_title(col, _(titles[i]));
+ gtk_tree_view_column_set_resizable(col, TRUE);
+ gtk_tree_view_column_set_sort_column_id(col, i);
+ gtk_tree_view_column_set_reorderable(col, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(proctree), col);
+
+ // type
+ switch (i) {
+ case COL_MEMXSERVER:
+ gtk_tree_view_column_set_cell_data_func(col, cell,
+ &procman::size_cell_data_func,
+ GUINT_TO_POINTER(i),
+ NULL);
+ break;
+
+ case COL_VMSIZE:
+ case COL_MEMRES:
+ case COL_MEMSHARED:
+ case COL_MEM:
+ case COL_MEMWRITABLE:
+ gtk_tree_view_column_set_cell_data_func(col, cell,
+ &procman::size_na_cell_data_func,
+ GUINT_TO_POINTER(i),
+ NULL);
+ break;
+
+ case COL_CPU_TIME:
+ gtk_tree_view_column_set_cell_data_func(col, cell,
+ &procman::duration_cell_data_func,
+ GUINT_TO_POINTER(i),
+ NULL);
+ break;
+
+ case COL_START_TIME:
+ gtk_tree_view_column_set_cell_data_func(col, cell,
+ &procman::time_cell_data_func,
+ GUINT_TO_POINTER(i),
+ NULL);
+ break;
+
+ case COL_STATUS:
+ gtk_tree_view_column_set_cell_data_func(col, cell,
+ &procman::status_cell_data_func,
+ GUINT_TO_POINTER(i),
+ NULL);
+ break;
+
+ default:
+ gtk_tree_view_column_set_attributes(col, cell, "text", i, NULL);
+ break;
+ }
+
+ // xaling
+ switch(i)
+ {
+ case COL_VMSIZE:
+ case COL_MEMRES:
+ case COL_MEMWRITABLE:
+ case COL_MEMSHARED:
+ case COL_MEMXSERVER:
+ case COL_CPU:
+ case COL_NICE:
+ case COL_PID:
+ case COL_CPU_TIME:
+ case COL_MEM:
+ g_object_set(G_OBJECT(cell), "xalign", 1.0f, NULL);
+ break;
+ }
+
+ // sizing
+ switch (i) {
+ case COL_ARGS:
+ gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(col, 150);
+ break;
+ default:
+ gtk_tree_view_column_set_min_width(column, 20);
+ break;
+ }
+ }
+
+ gtk_container_add (GTK_CONTAINER (scrolled), proctree);
+
+ procdata->tree = proctree;
+
+ set_proctree_reorderable(procdata);
+
+ procman_get_tree_state (procdata->client, proctree, "/apps/procman/proctree");
+
+ /* Override column settings by hiding this column if it's meaningless: */
+ if (!can_show_security_context_column ()) {
+ GtkTreeViewColumn *column;
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (proctree), COL_SECURITYCONTEXT);
+ gtk_tree_view_column_set_visible (column, FALSE);
+ }
+
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (proctree))),
+ "changed",
+ G_CALLBACK (cb_row_selected), procdata);
+ g_signal_connect (G_OBJECT (proctree), "popup_menu",
+ G_CALLBACK (cb_tree_popup_menu), procdata);
+ g_signal_connect (G_OBJECT (proctree), "button_press_event",
+ G_CALLBACK (cb_tree_button_pressed), procdata);
+
+ g_signal_connect (G_OBJECT(proctree), "columns-changed",
+ G_CALLBACK(cb_columns_changed), procdata);
+
+ return scrolled;
+}
+
+
+ProcInfo::~ProcInfo()
+{
+ g_free(this->name);
+ g_free(this->tooltip);
+ g_free(this->arguments);
+ g_free(this->security_context);
+}
+
+
+static void
+get_process_name (ProcInfo *info,
+ const gchar *cmd, const GStrv args)
+{
+ if (args) {
+ // look for /usr/bin/very_long_name
+ // and also /usr/bin/interpreter /usr/.../very_long_name
+ // which may have use prctl to alter 'cmd' name
+ for (int i = 0; i != 2 && args[i]; ++i) {
+ char* basename;
+ basename = g_path_get_basename(args[i]);
+
+ if (g_str_has_prefix(basename, cmd)) {
+ info->name = basename;
+ return;
+ }
+
+ g_free(basename);
+ }
+ }
+
+ info->name = g_strdup (cmd);
+}
+
+
+
+void
+ProcInfo::set_user(guint uid)
+{
+ if (G_LIKELY(this->uid == uid))
+ return;
+
+ this->uid = uid;
+
+ typedef std::pair<ProcInfo::UserMap::iterator, bool> Pair;
+ ProcInfo::UserMap::value_type hint(uid, "");
+ Pair p(ProcInfo::users.insert(hint));
+
+ // procman_debug("User lookup for uid %u: %s", uid, (p.second ? "MISS" : "HIT"));
+
+ if (p.second) {
+ struct passwd* pwd;
+ pwd = getpwuid(uid);
+
+ if (pwd && pwd->pw_name)
+ p.first->second = pwd->pw_name;
+ else {
+ char username[16];
+ g_sprintf(username, "%u", uid);
+ p.first->second = username;
+ }
+ }
+
+ this->user = p.first->second;
+}
+
+
+
+static void get_process_memory_writable(ProcInfo *info)
+{
+ glibtop_proc_map buf;
+ glibtop_map_entry *maps;
+
+ maps = glibtop_get_proc_map(&buf, info->pid);
+
+ gulong memwritable = 0;
+ const unsigned number = buf.number;
+
+ for (unsigned i = 0; i < number; ++i) {
+#ifdef __linux__
+ memwritable += maps[i].private_dirty;
+#else
+ if (maps[i].perm & GLIBTOP_MAP_PERM_WRITE)
+ memwritable += maps[i].size;
+#endif
+ }
+
+ info->memwritable = memwritable;
+
+ g_free(maps);
+}
+
+
+static void
+get_process_memory_info(ProcInfo *info)
+{
+ glibtop_proc_mem procmem;
+ WnckResourceUsage xresources;
+
+ wnck_pid_read_resource_usage (gdk_screen_get_display (gdk_screen_get_default ()),
+ info->pid,
+ &xresources);
+
+ glibtop_get_proc_mem(&procmem, info->pid);
+
+ info->vmsize = procmem.vsize;
+ info->memres = procmem.resident;
+ info->memshared = procmem.share;
+
+ info->memxserver = xresources.total_bytes_estimate;
+
+ get_process_memory_writable(info);
+
+ // fake the smart memory column if writable is not available
+ info->mem = info->memxserver + (info->memwritable ? info->memwritable : info->memres);
+}
+
+
+
+static void
+update_info_mutable_cols(ProcInfo *info)
+{
+ ProcData * const procdata = ProcData::get_instance();
+ GtkTreeModel *model;
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(procdata->tree));
+
+ using procman::tree_store_update;
+
+ tree_store_update(model, &info->node, COL_STATUS, info->status);
+ tree_store_update(model, &info->node, COL_USER, info->user.c_str());
+ tree_store_update(model, &info->node, COL_VMSIZE, info->vmsize);
+ tree_store_update(model, &info->node, COL_MEMRES, info->memres);
+ tree_store_update(model, &info->node, COL_MEMWRITABLE, info->memwritable);
+ tree_store_update(model, &info->node, COL_MEMSHARED, info->memshared);
+ tree_store_update(model, &info->node, COL_MEMXSERVER, info->memxserver);
+ tree_store_update(model, &info->node, COL_CPU, info->pcpu);
+ tree_store_update(model, &info->node, COL_CPU_TIME, info->cpu_time);
+ tree_store_update(model, &info->node, COL_START_TIME, info->start_time);
+ tree_store_update(model, &info->node, COL_NICE, info->nice);
+ tree_store_update(model, &info->node, COL_MEM, info->mem);
+ tree_store_update(model, &info->node, COL_WCHAN, info->wchan);
+}
+
+
+
+static void
+insert_info_to_tree (ProcInfo *info, ProcData *procdata, bool forced = false)
+{
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (procdata->tree));
+
+ if (procdata->config.show_tree) {
+
+ ProcInfo *parent = 0;
+
+ if (not forced)
+ parent = ProcInfo::find(info->ppid);
+
+ if (parent) {
+ GtkTreePath *parent_node = gtk_tree_model_get_path(model, &parent->node);
+ gtk_tree_store_insert(GTK_TREE_STORE(model), &info->node, &parent->node, 0);
+
+ if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(procdata->tree), parent_node))
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(procdata->tree), parent_node, FALSE);
+ gtk_tree_path_free(parent_node);
+ } else
+ gtk_tree_store_insert(GTK_TREE_STORE(model), &info->node, NULL, 0);
+ }
+ else
+ gtk_tree_store_insert (GTK_TREE_STORE (model), &info->node, NULL, 0);
+
+ gtk_tree_store_set (GTK_TREE_STORE (model), &info->node,
+ COL_POINTER, info,
+ COL_NAME, info->name,
+ COL_ARGS, info->arguments,
+ COL_TOOLTIP, info->tooltip,
+ COL_PID, info->pid,
+ COL_SECURITYCONTEXT, info->security_context,
+ -1);
+
+ procdata->pretty_table.set_icon(*info);
+
+ procman_debug("inserted %d%s", info->pid, (forced ? " (forced)" : ""));
+}
+
+
+/* Removing a node with children - make sure the children are queued
+** to be readded.
+*/
+template<typename List>
+static void
+remove_info_from_tree (ProcData *procdata, GtkTreeModel *model,
+ ProcInfo *current, List &orphans, unsigned lvl = 0)
+{
+ GtkTreeIter child_node;
+
+ if (std::find(orphans.begin(), orphans.end(), current) != orphans.end()) {
+ procman_debug("[%u] %d already removed from tree", lvl, int(current->pid));
+ return;
+ }
+
+ procman_debug("[%u] pid %d, %d children", lvl, int(current->pid),
+ gtk_tree_model_iter_n_children(model, &current->node));
+
+ // it is not possible to iterate&erase over a treeview so instead we
+ // just pop one child after another and recursively remove it and
+ // its children
+
+ while (gtk_tree_model_iter_children(model, &child_node, &current->node)) {
+ ProcInfo *child = 0;
+ gtk_tree_model_get(model, &child_node, COL_POINTER, &child, -1);
+ remove_info_from_tree(procdata, model, child, orphans, lvl + 1);
+ }
+
+ g_assert(not gtk_tree_model_iter_has_child(model, &current->node));
+
+ if (procdata->selected_process == current)
+ procdata->selected_process = NULL;
+
+ orphans.push_back(current);
+ gtk_tree_store_remove(GTK_TREE_STORE(model), &current->node);
+ procman::poison(current->node, 0x69);
+}
+
+
+
+static void
+update_info (ProcData *procdata, ProcInfo *info)
+{
+ glibtop_proc_state procstate;
+ glibtop_proc_uid procuid;
+ glibtop_proc_time proctime;
+ glibtop_proc_kernel prockernel;
+
+ glibtop_get_proc_kernel(&prockernel, info->pid);
+ g_strlcpy(info->wchan, prockernel.wchan, sizeof info->wchan);
+
+ glibtop_get_proc_state (&procstate, info->pid);
+ info->status = procstate.state;
+
+ glibtop_get_proc_uid (&procuid, info->pid);
+ glibtop_get_proc_time (&proctime, info->pid);
+
+ get_process_memory_info(info);
+
+ info->set_user(procstate.uid);
+
+ info->pcpu = (proctime.rtime - info->cpu_time) * 100 / procdata->cpu_total_time;
+ info->pcpu = MIN(info->pcpu, 100);
+
+ if (not procdata->config.solaris_mode)
+ info->pcpu *= procdata->config.num_cpus;
+
+ ProcInfo::cpu_times[info->pid] = info->cpu_time = proctime.rtime;
+ info->nice = procuid.nice;
+ info->ppid = procuid.ppid;
+}
+
+
+ProcInfo::ProcInfo(pid_t pid)
+ : tooltip(NULL),
+ name(NULL),
+ arguments(NULL),
+ security_context(NULL),
+ pid(pid),
+ uid(-1)
+{
+ ProcInfo * const info = this;
+ glibtop_proc_state procstate;
+ glibtop_proc_time proctime;
+ glibtop_proc_args procargs;
+ gchar** arguments;
+
+ glibtop_get_proc_state (&procstate, pid);
+ glibtop_get_proc_time (&proctime, pid);
+ arguments = glibtop_get_proc_argv (&procargs, pid, 0);
+
+ /* FIXME : wrong. name and arguments may change with exec* */
+ get_process_name (info, procstate.cmd, static_cast<const GStrv>(arguments));
+
+ std::string tooltip = make_string(g_strjoinv(" ", arguments));
+ if (tooltip.empty())
+ tooltip = procstate.cmd;
+
+ info->tooltip = g_markup_escape_text(tooltip.c_str(), -1);
+
+ info->arguments = g_strescape(tooltip.c_str(), "\\\"");
+ g_strfreev(arguments);
+
+ guint64 cpu_time = proctime.rtime;
+ std::map<pid_t, guint64>::iterator it(ProcInfo::cpu_times.find(pid));
+ if (it != ProcInfo::cpu_times.end())
+ {
+ if (proctime.rtime >= it->second)
+ cpu_time = it->second;
+ }
+ info->cpu_time = cpu_time;
+ info->start_time = proctime.start_time;
+
+ get_process_selinux_context (info);
+}
+
+
+
+
+static void
+refresh_list (ProcData *procdata, const pid_t* pid_list, const guint n)
+{
+ typedef std::list<ProcInfo*> ProcList;
+ ProcList addition;
+
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (procdata->tree));
+ guint i;
+
+ // Add or update processes in the process list
+ for(i = 0; i < n; ++i) {
+ ProcInfo *info = ProcInfo::find(pid_list[i]);
+
+ if (!info) {
+ info = new ProcInfo(pid_list[i]);
+ ProcInfo::all[info->pid] = info;
+ addition.push_back(info);
+ }
+
+ update_info (procdata, info);
+ }
+
+
+ // Remove dead processes from the process list and from the
+ // tree. children are queued to be readded at the right place
+ // in the tree.
+
+ const std::set<pid_t> pids(pid_list, pid_list + n);
+
+ ProcInfo::Iterator it(ProcInfo::begin());
+
+ while (it != ProcInfo::end()) {
+ ProcInfo * const info = it->second;
+ ProcInfo::Iterator next(it);
+ ++next;
+
+ if (pids.find(info->pid) == pids.end()) {
+ procman_debug("ripping %d", info->pid);
+ remove_info_from_tree(procdata, model, info, addition);
+ addition.remove(info);
+ ProcInfo::all.erase(it);
+ delete info;
+ }
+
+ it = next;
+ }
+
+ // INVARIANT
+ // pid_list == ProcInfo::all + addition
+
+
+ if (procdata->config.show_tree) {
+
+ // insert process in the tree. walk through the addition list
+ // (new process + process that have a new parent). This loop
+ // handles the dependencies because we cannot insert a process
+ // until its parent is in the tree.
+
+ std::set<pid_t> in_tree(pids);
+
+ for (ProcList::iterator it(addition.begin()); it != addition.end(); ++it)
+ in_tree.erase((*it)->pid);
+
+
+ while (not addition.empty()) {
+ procman_debug("looking for %d parents", int(addition.size()));
+ ProcList::iterator it(addition.begin());
+
+ while (it != addition.end()) {
+ procman_debug("looking for %d's parent with ppid %d",
+ int((*it)->pid), int((*it)->ppid));
+
+
+ // inserts the process in the treeview if :
+ // - it is init
+ // - its parent is already in tree
+ // - its parent is unreachable
+ //
+ // rounds == 2 means that addition contains processes with
+ // unreachable parents
+ //
+ // FIXME: this is broken if the unreachable parent becomes active
+ // i.e. it gets active or changes ower
+ // so we just clear the tree on __each__ update
+ // see proctable_update_list (ProcData * const procdata)
+
+
+ if ((*it)->ppid == 0 or in_tree.find((*it)->ppid) != in_tree.end()) {
+ insert_info_to_tree(*it, procdata);
+ in_tree.insert((*it)->pid);
+ it = addition.erase(it);
+ continue;
+ }
+
+ ProcInfo *parent = ProcInfo::find((*it)->ppid);
+ // if the parent is unreachable
+ if (not parent) {
+ // or std::find(addition.begin(), addition.end(), parent) == addition.end()) {
+ insert_info_to_tree(*it, procdata, true);
+ in_tree.insert((*it)->pid);
+ it = addition.erase(it);
+ continue;
+ }
+
+ ++it;
+ }
+ }
+ }
+ else {
+ // don't care of the tree
+ for (ProcList::iterator it(addition.begin()); it != addition.end(); ++it)
+ insert_info_to_tree(*it, procdata);
+ }
+
+
+ for (ProcInfo::Iterator it(ProcInfo::begin()); it != ProcInfo::end(); ++it)
+ update_info_mutable_cols(it->second);
+}
+
+
+void
+proctable_update_list (ProcData * const procdata)
+{
+ pid_t* pid_list;
+ glibtop_proclist proclist;
+ glibtop_cpu cpu;
+ gint which, arg;
+ procman::SelectionMemento selection;
+
+ switch (procdata->config.whose_process) {
+ case ALL_PROCESSES:
+ which = GLIBTOP_KERN_PROC_ALL;
+ arg = 0;
+ break;
+
+ case ACTIVE_PROCESSES:
+ which = GLIBTOP_KERN_PROC_ALL | GLIBTOP_EXCLUDE_IDLE;
+ arg = 0;
+ if (procdata->config.show_tree)
+ {
+ selection.save(procdata->tree);
+ proctable_clear_tree(procdata);
+ }
+ break;
+
+ default:
+ which = GLIBTOP_KERN_PROC_UID;
+ arg = getuid ();
+ if (procdata->config.show_tree)
+ {
+ selection.save(procdata->tree);
+ proctable_clear_tree(procdata);
+ }
+ break;
+ }
+
+ pid_list = glibtop_get_proclist (&proclist, which, arg);
+
+ /* FIXME: total cpu time elapsed should be calculated on an individual basis here
+ ** should probably have a total_time_last gint in the ProcInfo structure */
+ glibtop_get_cpu (&cpu);
+ procdata->cpu_total_time = MAX(cpu.total - procdata->cpu_total_time_last, 1);
+ procdata->cpu_total_time_last = cpu.total;
+
+ refresh_list (procdata, pid_list, proclist.number);
+
+ selection.restore(procdata->tree);
+
+ g_free (pid_list);
+
+ /* proclist.number == g_list_length(procdata->info) == g_hash_table_size(procdata->pids) */
+}
+
+
+void
+proctable_update_all (ProcData * const procdata)
+{
+ char* string;
+
+ string = make_loadavg_string();
+ gtk_label_set_text (GTK_LABEL(procdata->loadavg), string);
+ g_free (string);
+
+ proctable_update_list (procdata);
+}
+
+
+void
+proctable_clear_tree (ProcData * const procdata)
+{
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (procdata->tree));
+
+ gtk_tree_store_clear (GTK_TREE_STORE (model));
+
+ proctable_free_table (procdata);
+
+ update_sensitivity(procdata);
+}
+
+
+void
+proctable_free_table (ProcData * const procdata)
+{
+ for (ProcInfo::Iterator it(ProcInfo::begin()); it != ProcInfo::end(); ++it)
+ delete it->second;
+
+ ProcInfo::all.clear();
+}
+
+
+
+char*
+make_loadavg_string(void)
+{
+ glibtop_loadavg buf;
+
+ glibtop_get_loadavg(&buf);
+
+ return g_strdup_printf(
+ _("Load averages for the last 1, 5, 15 minutes: "
+ "%0.2f, %0.2f, %0.2f"),
+ buf.loadavg[0],
+ buf.loadavg[1],
+ buf.loadavg[2]);
+}
+
+
+
+void
+ProcInfo::set_icon(Glib::RefPtr<Gdk::Pixbuf> icon)
+{
+ this->pixbuf = icon;
+
+ GtkTreeModel *model;
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(ProcData::get_instance()->tree));
+ gtk_tree_store_set(GTK_TREE_STORE(model), &this->node,
+ COL_PIXBUF, (this->pixbuf ? this->pixbuf->gobj() : NULL),
+ -1);
+}
diff --git a/src/proctable.h b/src/proctable.h
new file mode 100644
index 0000000..2cd1def
--- /dev/null
+++ b/src/proctable.h
@@ -0,0 +1,65 @@
+/* Procman - tree view
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _PROCMAN_PROCTABLE_H_
+#define _PROCMAN_PROCTABLE_H_
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "procman.h"
+
+enum
+{
+ COL_NAME = 0,
+ COL_USER,
+ COL_STATUS,
+ COL_VMSIZE,
+ COL_MEMRES,
+ COL_MEMWRITABLE,
+ COL_MEMSHARED,
+ COL_MEMXSERVER,
+ COL_CPU,
+ COL_CPU_TIME,
+ COL_START_TIME,
+ COL_NICE,
+ COL_PID,
+ COL_SECURITYCONTEXT,
+ COL_ARGS,
+ COL_MEM,
+ COL_WCHAN,
+ COL_PIXBUF,
+ COL_POINTER,
+ COL_TOOLTIP,
+ NUM_COLUMNS
+};
+
+
+GtkWidget* proctable_new (ProcData *data);
+void proctable_update_table (ProcData *data);
+void proctable_update_list (ProcData *data);
+void proctable_update_all (ProcData *data);
+void proctable_clear_tree (ProcData *data);
+void proctable_free_table (ProcData *data);
+
+GSList* proctable_get_columns_order(GtkTreeView *treeview);
+void proctable_set_columns_order(GtkTreeView *treeview, GSList *order);
+
+char* make_loadavg_string(void);
+
+#endif /* _PROCMAN_PROCTABLE_H_ */
diff --git a/src/selection.cpp b/src/selection.cpp
new file mode 100644
index 0000000..6f293a6
--- /dev/null
+++ b/src/selection.cpp
@@ -0,0 +1,43 @@
+#include <config.h>
+
+#include "selection.h"
+#include "proctable.h"
+#include "util.h"
+
+namespace procman
+{
+ void SelectionMemento::add_to_selected(GtkTreeModel* model, GtkTreePath*, GtkTreeIter* iter, gpointer data)
+ {
+ guint pid = 0;
+ gtk_tree_model_get(model, iter, COL_PID, &pid, -1);
+ if (pid)
+ static_cast<SelectionMemento*>(data)->pids.push_back(pid);
+ }
+
+
+ void SelectionMemento::save(GtkWidget* tree)
+ {
+ GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+ gtk_tree_selection_selected_foreach(selection, &SelectionMemento::add_to_selected, this);
+ }
+
+
+ void SelectionMemento::restore(GtkWidget* tree)
+ {
+ if (not this->pids.empty())
+ {
+ GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+ typedef std::vector<pid_t>::iterator iterator;
+ for (iterator it(this->pids.begin()); it != this->pids.end(); ++it)
+ {
+ if (ProcInfo* proc = ProcInfo::find(*it))
+ {
+ gtk_tree_selection_select_iter(selection, &proc->node);
+ procman_debug("Re-selected process %u", unsigned(*it));
+ }
+ else
+ procman_debug("Could not find process %u, cannot re-select it", unsigned(*it));
+ }
+ }
+ }
+}
diff --git a/src/selection.h b/src/selection.h
new file mode 100644
index 0000000..1dd3470
--- /dev/null
+++ b/src/selection.h
@@ -0,0 +1,21 @@
+#ifndef H_MATE_SYSTEM_MONITOR_SELECTION_H_1183113337
+#define H_MATE_SYSTEM_MONITOR_SELECTION_H_1183113337
+
+#include <sys/types.h>
+#include <gtk/gtk.h>
+#include <vector>
+
+namespace procman
+{
+ class SelectionMemento
+ {
+ std::vector<pid_t> pids;
+ static void add_to_selected(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer data);
+
+ public:
+ void save(GtkWidget* tree);
+ void restore(GtkWidget* tree);
+ };
+}
+
+#endif /* H_MATE_SYSTEM_MONITOR_SELECTION_H_1183113337 */
diff --git a/src/selinux.cpp b/src/selinux.cpp
new file mode 100644
index 0000000..6bfee5a
--- /dev/null
+++ b/src/selinux.cpp
@@ -0,0 +1,65 @@
+#include <config.h>
+
+#include <glib.h>
+
+#include "selinux.h"
+#include "procman.h"
+#include "util.h"
+
+
+static int (*getpidcon)(pid_t, char**);
+static void (*freecon)(char*);
+static int (*is_selinux_enabled)(void);
+
+static gboolean has_selinux;
+
+static gboolean load_selinux(void)
+{
+ return load_symbols("libselinux.so.1",
+ "getpidcon", &getpidcon,
+ "freecon", &freecon,
+ "is_selinux_enabled", &is_selinux_enabled,
+ NULL);
+}
+
+
+
+void
+get_process_selinux_context (ProcInfo *info)
+{
+ char *con;
+
+ if (has_selinux && !getpidcon (info->pid, &con)) {
+ info->security_context = g_strdup (con);
+ freecon (con);
+ }
+}
+
+
+
+gboolean
+can_show_security_context_column (void)
+{
+ if (!(has_selinux = load_selinux()))
+ return FALSE;
+
+ switch (is_selinux_enabled()) {
+ case 1:
+ /* We're running on an SELinux kernel */
+ return TRUE;
+
+ case -1:
+ /* Error; hide the security context column */
+
+ case 0:
+ /* We're not running on an SELinux kernel:
+ hide the security context column */
+
+ default:
+ g_warning("SELinux was found but is not enabled.\n");
+ return FALSE;
+ }
+}
+
+
+
diff --git a/src/selinux.h b/src/selinux.h
new file mode 100644
index 0000000..be79ce9
--- /dev/null
+++ b/src/selinux.h
@@ -0,0 +1,14 @@
+#ifndef PROCMAN_SELINUX_H_20050525
+#define PROCMAN_SELINUX_H_20050525
+
+#include <glib.h>
+
+#include "procman.h"
+
+void
+get_process_selinux_context (ProcInfo *info);
+
+gboolean
+can_show_security_context_column (void) G_GNUC_CONST;
+
+#endif /* PROCMAN_SELINUX_H_20050525 */
diff --git a/src/smooth_refresh.cpp b/src/smooth_refresh.cpp
new file mode 100644
index 0000000..f21c023
--- /dev/null
+++ b/src/smooth_refresh.cpp
@@ -0,0 +1,173 @@
+#include <config.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <mateconf/mateconf-client.h>
+#include <glibtop.h>
+#include <glibtop/proctime.h>
+#include <glibtop/cpu.h>
+
+#include <algorithm>
+
+#include "smooth_refresh.h"
+#include "procman.h"
+#include "util.h"
+
+
+const string SmoothRefresh::KEY("/apps/procman/smooth_refresh");
+const bool SmoothRefresh::KEY_DEFAULT_VALUE(true);
+
+
+
+unsigned SmoothRefresh::get_own_cpu_usage()
+{
+ glibtop_cpu cpu;
+ glibtop_proc_time proctime;
+ guint64 elapsed;
+ unsigned usage = PCPU_LO;
+
+ glibtop_get_cpu (&cpu);
+ elapsed = cpu.total - this->last_total_time;
+
+ if (elapsed) { // avoid division by 0
+ glibtop_get_proc_time(&proctime, getpid());
+ usage = (proctime.rtime - this->last_cpu_time) * 100 / elapsed;
+ }
+
+ usage = CLAMP(usage, 0, 100);
+
+ this->last_total_time = cpu.total;
+ this->last_cpu_time = proctime.rtime;
+
+ return usage;
+}
+
+
+
+void SmoothRefresh::status_changed(MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ static_cast<SmoothRefresh*>(user_data)->load_mateconf_value(mateconf_entry_get_value(entry));
+}
+
+void SmoothRefresh::load_mateconf_value(MateConfValue* value)
+{
+ bool own_value = false;
+
+ if (not value) {
+ value = mateconf_client_get(mateconf_client_get_default(), KEY.c_str(), NULL);
+ }
+
+ this->active = value ? mateconf_value_get_bool(value) : KEY_DEFAULT_VALUE;
+
+ if (this->active)
+ procman_debug("smooth_refresh is enabled");
+
+ if (own_value and value)
+ mateconf_value_free(value);
+}
+
+
+SmoothRefresh::SmoothRefresh()
+{
+ this->connection = mateconf_client_notify_add(mateconf_client_get_default(),
+ KEY.c_str(),
+ status_changed,
+ this,
+ NULL,
+ NULL);
+
+ this->reset();
+ this->load_mateconf_value();
+}
+
+
+
+void SmoothRefresh::reset()
+{
+ glibtop_cpu cpu;
+ glibtop_proc_time proctime;
+
+ glibtop_get_cpu(&cpu);
+ glibtop_get_proc_time(&proctime, getpid());
+
+ this->interval = ProcData::get_instance()->config.update_interval;
+ this->last_pcpu = PCPU_LO;
+ this->last_total_time = cpu.total;
+ this->last_cpu_time = proctime.rtime;
+}
+
+
+
+SmoothRefresh::~SmoothRefresh()
+{
+ if (this->connection)
+ mateconf_client_notify_remove(mateconf_client_get_default(),
+ this->connection);
+}
+
+
+
+bool
+SmoothRefresh::get(guint &new_interval)
+{
+ const unsigned config_interval = ProcData::get_instance()->config.update_interval;
+
+ g_assert(this->interval >= config_interval);
+
+ if (not this->active)
+ return false;
+
+
+ const unsigned pcpu = this->get_own_cpu_usage();
+ /*
+ invariant: MAX_UPDATE_INTERVAL >= interval >= config_interval >= MIN_UPDATE_INTERVAL
+
+ i see 3 cases:
+
+ a) interval is too big (CPU usage < 10%)
+ -> increase interval
+
+ b) interval is too small (CPU usage > 10%) AND interval != config_interval
+ >
+ -> decrease interval
+
+ c) interval is config_interval (start or interval is perfect)
+
+ */
+
+ if (pcpu > PCPU_HI && this->last_pcpu > PCPU_HI)
+ new_interval = this->interval * 11 / 10;
+ else if (this->interval != config_interval && pcpu < PCPU_LO && this->last_pcpu < PCPU_LO)
+ new_interval = this->interval * 9 / 10;
+ else
+ new_interval = this->interval;
+
+ new_interval = CLAMP(new_interval, config_interval, config_interval * 2);
+ new_interval = CLAMP(new_interval, MIN_UPDATE_INTERVAL, MAX_UPDATE_INTERVAL);
+
+ bool changed = this->interval != new_interval;
+
+ if (changed)
+ this->interval = new_interval;
+
+ this->last_pcpu = pcpu;
+
+
+ if (changed) {
+ procman_debug("CPU usage is %3u%%, changed refresh_interval to %u (config %u)",
+ this->last_pcpu,
+ this->interval,
+ config_interval);
+ }
+
+ g_assert(this->interval == new_interval);
+ g_assert(this->interval >= config_interval);
+
+ return changed;
+}
+
diff --git a/src/smooth_refresh.h b/src/smooth_refresh.h
new file mode 100644
index 0000000..29a503f
--- /dev/null
+++ b/src/smooth_refresh.h
@@ -0,0 +1,104 @@
+#ifndef _PROCMAN_SMOOTH_REFRESH
+#define _PROCMAN_SMOOTH_REFRESH
+
+#include <glib.h>
+#include <mateconf/mateconf-client.h>
+
+#include <string>
+
+using std::string;
+
+
+
+class SmoothRefresh
+{
+public:
+
+ /*
+ smooth_refresh_new
+
+ @config_interval : pointer to config_interval so we can observe
+ config_interval changes.
+
+ @return : initialized SmoothRefresh
+ */
+ SmoothRefresh();
+
+ ~SmoothRefresh();
+
+ /*
+ smooth_refresh_reset
+
+ Resets state and re-read config_interval
+ */
+ void reset();
+
+ /*
+ smooth_refresh_get
+
+ Computes the new refresh_interval so that CPU usage is lower than
+ SMOOTH_REFRESH_PCPU.
+
+ @new_interval : where the new refresh_interval is stored.
+
+ @return : TRUE is refresh_interval has changed. The new refresh_interval
+ is stored in @new_interval. Else FALSE;
+ */
+ bool get(guint &new_interval);
+
+
+ static const string KEY;
+ static const bool KEY_DEFAULT_VALUE;
+
+private:
+
+ unsigned get_own_cpu_usage();
+
+ static void status_changed(MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data);
+
+ void load_mateconf_value(MateConfValue* value = NULL);
+
+ /*
+ fuzzy logic:
+ - decrease refresh interval only if current CPU% and last CPU%
+ are higher than PCPU_LO
+ - increase refresh interval only if current CPU% and last CPU%
+ are higher than PCPU_HI
+
+ */
+
+ enum
+ {
+ PCPU_HI = 22,
+ PCPU_LO = 18
+ };
+
+ /*
+ -self : procman's PID (so we call getpid() only once)
+
+ -interval : current refresh interval
+
+ -config_interval : pointer to the configuration refresh interval.
+ Used to watch configuration changes
+
+ -interval >= -config_interval
+
+ -last_pcpu : to avoid spikes, the last CPU%. See PCPU_{LO,HI}
+
+ -last_total_time:
+ -last_cpu_time: Save last cpu and process times to compute CPU%
+ */
+
+ bool active;
+ guint connection;
+ guint interval;
+ unsigned last_pcpu;
+ guint64 last_total_time;
+ guint64 last_cpu_time;
+};
+
+
+#endif /* _PROCMAN_SMOOTH_REFRESH */
diff --git a/src/sysinfo.cpp b/src/sysinfo.cpp
new file mode 100644
index 0000000..9ddf31c
--- /dev/null
+++ b/src/sysinfo.cpp
@@ -0,0 +1,676 @@
+#include <config.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <glibmm.h>
+#include <glib/gi18n.h>
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <glibtop/fsusage.h>
+#include <glibtop/mountlist.h>
+#include <glibtop/mem.h>
+#include <glibtop/sysinfo.h>
+
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <math.h>
+#include <errno.h>
+
+#include <string>
+#include <vector>
+#include <fstream>
+#include <sstream>
+#include <sys/utsname.h>
+
+#include "sysinfo.h"
+#include "procman.h"
+#include "util.h"
+
+
+using std::string;
+using std::vector;
+
+
+namespace {
+
+
+ class SysInfo
+ {
+ public:
+ string hostname;
+ string distro_name;
+ string distro_release;
+ string kernel;
+ string mate_version;
+ guint64 memory_bytes;
+ guint64 free_space_bytes;
+
+ guint n_processors;
+ vector<string> processors;
+
+
+ SysInfo()
+ {
+ this->load_processors_info();
+ this->load_memory_info();
+ this->load_disk_info();
+ this->load_uname_info();
+ this->load_mate_version();
+ }
+
+ virtual ~SysInfo()
+ { }
+
+ virtual void set_distro_labels(GtkWidget* name, GtkWidget* release)
+ {
+ g_object_set(G_OBJECT(name),
+ "label",
+ ("<big><big><b>" + this->distro_name + "</b></big></big>").c_str(),
+ NULL);
+
+
+ char* markup = g_strdup_printf(_("Release %s"), this->distro_release.c_str());
+
+ g_object_set(G_OBJECT(release),
+ "label",
+ markup,
+ NULL);
+
+ g_free(markup);
+ }
+
+ static string system()
+ {
+ return uname().sysname;
+ }
+
+ private:
+
+ void load_memory_info()
+ {
+ glibtop_mem mem;
+
+ glibtop_get_mem(&mem);
+ this->memory_bytes = mem.total;
+ }
+
+
+ void load_processors_info()
+ {
+ const glibtop_sysinfo *info = glibtop_get_sysinfo();
+
+ for (guint i = 0; i != info->ncpu; ++i) {
+ const char * const keys[] = { "model name", "cpu" };
+ gchar *model = 0;
+
+ for (guint j = 0; !model && j != G_N_ELEMENTS(keys); ++j)
+ model = static_cast<char*>(g_hash_table_lookup(info->cpuinfo[i].values,
+ keys[j]));
+
+ if (!model)
+ model = _("Unknown CPU model");
+
+ this->processors.push_back(model);
+ }
+ }
+
+
+
+ void load_disk_info()
+ {
+ glibtop_mountentry *entries;
+ glibtop_mountlist mountlist;
+
+ entries = glibtop_get_mountlist(&mountlist, 0);
+
+ this->free_space_bytes = 0;
+
+ for (guint i = 0; i != mountlist.number; ++i) {
+
+ if (string(entries[i].devname).find("/dev/") != 0)
+ continue;
+
+ if (string(entries[i].mountdir).find("/media/") == 0)
+ continue;
+
+ glibtop_fsusage usage;
+ glibtop_get_fsusage(&usage, entries[i].mountdir);
+ this->free_space_bytes += usage.bavail * usage.block_size;
+ }
+
+ g_free(entries);
+ }
+
+ static const struct utsname & uname()
+ {
+ static struct utsname name;
+
+ if (!name.sysname[0]) {
+ ::uname(&name);
+ }
+
+ return name;
+ }
+
+ void load_uname_info()
+ {
+ this->hostname = uname().nodename;
+ this->kernel = string(uname().sysname) + ' ' + uname().release;
+ }
+
+
+ void load_mate_version()
+ {
+ xmlDocPtr document;
+ xmlXPathContextPtr context;
+ const string nodes[3] = { "string(/mate-version/platform)",
+ "string(/mate-version/minor)",
+ "string(/mate-version/micro)" };
+ string values[3];
+
+ if (not (document = xmlParseFile(DATADIR "/mate-about/mate-version.xml")))
+ return;
+
+ if (not (context = xmlXPathNewContext(document)))
+ return;
+
+ for (size_t i = 0; i != 3; ++i)
+ {
+ xmlXPathObjectPtr xpath;
+ xpath = xmlXPathEvalExpression(BAD_CAST nodes[i].c_str(), context);
+
+ if (xpath and xpath->type == XPATH_STRING)
+ values[i] = reinterpret_cast<const char*>(xpath->stringval);
+
+ xmlXPathFreeObject(xpath);
+ }
+
+ xmlXPathFreeContext(context);
+ xmlFreeDoc(document);
+
+ this->mate_version = values[0] + '.' + values[1] + '.' + values[2];
+ }
+ };
+
+
+
+ class SolarisSysInfo
+ : public SysInfo
+ {
+ public:
+ SolarisSysInfo()
+ {
+ this->load_solaris_info();
+ }
+
+ private:
+ void load_solaris_info()
+ {
+ this->distro_name = "Solaris";
+
+ std::ifstream input("/etc/release");
+
+ if (input)
+ std::getline(input, this->distro_release);
+ }
+ };
+
+
+ class LSBSysInfo
+ : public SysInfo
+ {
+ public:
+ LSBSysInfo()
+ : re(Glib::Regex::create("^.+?:\\s*(.+)\\s*$"))
+ {
+ // start();
+ }
+
+ virtual void set_distro_labels(GtkWidget* name, GtkWidget* release)
+ {
+ this->name = name;
+ this->release = release;
+
+ this->start();
+ }
+
+
+ private:
+
+ sigc::connection child_watch;
+ int lsb_fd;
+ GtkWidget* name;
+ GtkWidget* release;
+
+ void strip_description(string &s) const
+ {
+ const GRegexMatchFlags flags = static_cast<GRegexMatchFlags>(0);
+ GMatchInfo* info = 0;
+
+ if (g_regex_match(this->re->gobj(), s.c_str(), flags, &info)) {
+ s = make_string(g_match_info_fetch(info, 1));
+ g_match_info_free(info);
+ }
+ }
+
+ std::istream& get_value(std::istream &is, string &s) const
+ {
+ if (std::getline(is, s))
+ this->strip_description(s);
+ return is;
+ }
+
+
+ void read_lsb(Glib::Pid pid, int status)
+ {
+ this->child_watch.disconnect();
+
+ if (!WIFEXITED(status) or WEXITSTATUS(status) != 0) {
+ g_error("Child %d failed with status %d", int(pid), status);
+ return;
+ }
+
+ Glib::RefPtr<Glib::IOChannel> channel = Glib::IOChannel::create_from_fd(this->lsb_fd);
+ Glib::ustring content;
+
+ while (channel->read_to_end(content) == Glib::IO_STATUS_AGAIN)
+ ;
+
+ channel->close();
+ Glib::spawn_close_pid(pid);
+
+ procman_debug("lsb_release output = '%s'", content.c_str());
+
+ string release, codename;
+ std::istringstream input(content);
+
+ this->get_value(input, this->distro_name)
+ and this->get_value(input, release)
+ and this->get_value(input, codename);
+
+ this->distro_release = release;
+ if (codename != "" && codename != "n/a")
+ this->distro_release += " (" + codename + ')';
+
+ this->SysInfo::set_distro_labels(this->name, this->release);
+ }
+
+
+ void start()
+ {
+ std::vector<string> argv(2);
+ argv[0] = "lsb_release";
+ argv[1] = "-irc";
+
+ Glib::SpawnFlags flags = Glib::SPAWN_DO_NOT_REAP_CHILD
+ | Glib::SPAWN_SEARCH_PATH
+ | Glib::SPAWN_STDERR_TO_DEV_NULL;
+
+ Glib::Pid child;
+
+ try {
+ Glib::spawn_async_with_pipes("/", // wd
+ argv,
+ flags,
+ sigc::slot<void>(), // child setup
+ &child,
+ 0, // stdin
+ &this->lsb_fd); // stdout
+ } catch (Glib::SpawnError &e) {
+ g_error("g_spawn_async_with_pipes error: %s", e.what().c_str());
+ return;
+ }
+
+ sigc::slot<void,GPid, int> slot = sigc::mem_fun(this, &LSBSysInfo::read_lsb);
+ this->child_watch = Glib::signal_child_watch().connect(slot, child);
+ }
+
+
+ void sync_lsb_release()
+ {
+ char *out= 0;
+ GError *error = 0;
+ int status;
+
+ if (g_spawn_command_line_sync("lsb_release -irc",
+ &out,
+ 0,
+ &status,
+ &error)) {
+ string release, codename;
+ if (!error and WIFEXITED(status) and WEXITSTATUS(status) == 0) {
+ std::istringstream input(out);
+ this->get_value(input, this->distro_name)
+ and this->get_value(input, release)
+ and this->get_value(input, codename);
+ this->distro_release = release;
+ if (codename != "" && codename != "n/a")
+ this->distro_release += " (" + codename + ')';
+ }
+ }
+
+ if (error)
+ g_error_free(error);
+
+ g_free(out);
+ }
+
+ private:
+ Glib::RefPtr<Glib::Regex> re;
+ };
+
+
+ class NetBSDSysInfo
+ : public SysInfo
+ {
+ public:
+ NetBSDSysInfo()
+ {
+ this->load_netbsd_info();
+ }
+
+ private:
+ void load_netbsd_info()
+ {
+ this->distro_name = "NetBSD";
+
+ std::ifstream input("/etc/release");
+
+ if (input)
+ std::getline(input, this->distro_release);
+ }
+ };
+
+
+ SysInfo* get_sysinfo()
+ {
+ if (char *p = g_find_program_in_path("lsb_release")) {
+ g_free(p);
+ return new LSBSysInfo;
+ }
+ else if (SysInfo::system() == "SunOS") {
+ return new SolarisSysInfo;
+ }
+ else if (SysInfo::system() == "NetBSD") {
+ return new NetBSDSysInfo;
+ }
+
+ return new SysInfo;
+ }
+}
+
+
+#define X_PAD 5
+#define Y_PAD 12
+#define LOGO_W 92
+#define LOGO_H 351
+#define RADIUS 5
+
+static gboolean
+sysinfo_logo_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer data_ptr)
+{
+ GtkAllocation allocation;
+ GtkStyle *style;
+ cairo_t *cr;
+ cairo_pattern_t *cp;
+
+ cr = gdk_cairo_create(gtk_widget_get_window(widget));
+
+ gtk_widget_get_allocation (widget, &allocation);
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_move_to(cr, X_PAD + RADIUS, Y_PAD);
+ cairo_line_to(cr, X_PAD + LOGO_W - RADIUS, Y_PAD);
+ cairo_arc(cr, X_PAD + LOGO_W - RADIUS, Y_PAD + RADIUS, RADIUS, -0.5 * M_PI, 0);
+ cairo_line_to(cr, X_PAD + LOGO_W, Y_PAD + LOGO_H - RADIUS);
+ cairo_arc(cr, X_PAD + LOGO_W - RADIUS, Y_PAD + LOGO_H - RADIUS, RADIUS, 0, 0.5 * M_PI);
+ cairo_line_to(cr, X_PAD + RADIUS, Y_PAD + LOGO_H);
+ cairo_arc(cr, X_PAD + RADIUS, Y_PAD + LOGO_H - RADIUS, RADIUS, 0.5 * M_PI, -1.0 * M_PI);
+ cairo_line_to(cr, X_PAD, Y_PAD + RADIUS);
+ cairo_arc(cr, X_PAD + RADIUS, Y_PAD + RADIUS, RADIUS, -1.0 * M_PI, -0.5 * M_PI);
+
+ cp = cairo_pattern_create_linear(0, Y_PAD, 0, Y_PAD + LOGO_H);
+ style = gtk_widget_get_style (widget);
+ cairo_pattern_add_color_stop_rgba(cp, 0.0,
+ style->base[GTK_STATE_SELECTED].red / 65535.0,
+ style->base[GTK_STATE_SELECTED].green / 65535.0,
+ style->base[GTK_STATE_SELECTED].blue / 65535.0,
+ 1.0);
+ cairo_pattern_add_color_stop_rgba(cp, 1.0,
+ style->base[GTK_STATE_SELECTED].red / 65535.0,
+ style->base[GTK_STATE_SELECTED].green / 65535.0,
+ style->base[GTK_STATE_SELECTED].blue / 65535.0,
+ 0.0);
+ cairo_set_source(cr, cp);
+ cairo_fill(cr);
+
+ cairo_pattern_destroy(cp);
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+
+static GtkWidget*
+add_section(GtkBox *vbox , const char * title, int num_row, int num_col, GtkWidget **out_frame)
+{
+ GtkWidget *table;
+
+ GtkWidget *frame = gtk_frame_new(title);
+ gtk_frame_set_label_align(GTK_FRAME(frame), 0.0, 0.5);
+ gtk_label_set_use_markup(
+ GTK_LABEL(gtk_frame_get_label_widget(GTK_FRAME(frame))),
+ TRUE
+ );
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
+ gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+ GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
+ gtk_container_add(GTK_CONTAINER(frame), alignment);
+
+ table = gtk_table_new(num_row, num_col, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 6);
+ gtk_container_add(GTK_CONTAINER(alignment), table);
+
+ if(out_frame)
+ *out_frame = frame;
+
+ return table;
+}
+
+
+static GtkWidget*
+add_row(GtkTable * table, const char * label, const char * value, int row)
+{
+ GtkWidget *header = gtk_label_new(label);
+ gtk_misc_set_alignment(GTK_MISC(header), 0.0, 0.5);
+ gtk_table_attach(
+ table, header,
+ 0, 1, row, row + 1,
+ GTK_FILL, GTK_FILL, 0, 0
+ );
+
+ GtkWidget *label_widget = gtk_label_new(value);
+ gtk_misc_set_alignment(GTK_MISC(label_widget), 0.0, 0.5);
+ gtk_table_attach(
+ table, label_widget,
+ 1, 2, row, row + 1,
+ GTK_FILL, GTK_FILL, 0, 0
+ );
+ return label_widget;
+}
+
+
+static GtkWidget *
+procman_create_sysinfo_view(void)
+{
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+
+ SysInfo *data = get_sysinfo();;
+
+ GtkWidget * logo;
+
+ GtkWidget *distro_frame;
+ GtkWidget *distro_release_label;
+ GtkWidget *distro_table;
+
+ GtkWidget *hardware_table;
+ GtkWidget *memory_label;
+ GtkWidget *processor_label;
+
+ GtkWidget *disk_space_table;
+ GtkWidget *disk_space_label;
+
+ GtkWidget *header;
+
+ gchar *markup;
+
+
+ hbox = gtk_hbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 6);
+
+ /* left-side logo */
+
+ logo = gtk_image_new_from_file(DATADIR "/pixmaps/mate-system-monitor/side.png");
+ gtk_misc_set_alignment(GTK_MISC(logo), 0.5, 0.0);
+ gtk_misc_set_padding(GTK_MISC(logo), 5, 12);
+ gtk_box_pack_start(GTK_BOX(hbox), logo, FALSE, FALSE, 0);
+
+ g_signal_connect(G_OBJECT(logo), "expose-event",
+ G_CALLBACK(sysinfo_logo_expose), NULL);
+
+ vbox = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
+
+ // hostname
+
+ markup = g_strdup_printf("<big><big><b><u>%s</u></b></big></big>",
+ data->hostname.c_str());
+ GtkWidget *hostname_frame = gtk_frame_new(markup);
+ g_free(markup);
+ gtk_frame_set_label_align(GTK_FRAME(hostname_frame), 0.0, 0.5);
+ gtk_label_set_use_markup(
+ GTK_LABEL(gtk_frame_get_label_widget(GTK_FRAME(hostname_frame))),
+ TRUE
+ );
+ gtk_frame_set_shadow_type(GTK_FRAME(hostname_frame), GTK_SHADOW_NONE);
+ gtk_box_pack_start(GTK_BOX(vbox), hostname_frame, FALSE, FALSE, 0);
+
+
+ /* distro section */
+
+ unsigned table_size = 2;
+ if (data->mate_version != "")
+ table_size++;
+ distro_table = add_section(GTK_BOX(vbox), "???", table_size, 1, &distro_frame);
+
+ unsigned table_count = 0;
+
+ distro_release_label = gtk_label_new("???");
+ gtk_misc_set_alignment(GTK_MISC(distro_release_label), 0.0, 0.5);
+ gtk_table_attach(
+ GTK_TABLE(distro_table), distro_release_label,
+ 0, 1, table_count, table_count+1,
+ GTK_FILL, GTK_FILL, 0, 0
+ );
+ table_count++;
+ data->set_distro_labels(gtk_frame_get_label_widget(GTK_FRAME(distro_frame)), distro_release_label);
+
+ markup = g_strdup_printf(_("Kernel %s"), data->kernel.c_str());
+ header = gtk_label_new(markup);
+ g_free(markup);
+ gtk_misc_set_alignment(GTK_MISC(header), 0.0, 0.5);
+ gtk_table_attach(
+ GTK_TABLE(distro_table), header,
+ 0, 1, table_count, table_count + 1,
+ GTK_FILL, GTK_FILL, 0, 0
+ );
+ table_count++;
+
+ if (data->mate_version != "")
+ {
+ markup = g_strdup_printf(_("MATE %s"), data->mate_version.c_str());
+ header = gtk_label_new(markup);
+ g_free(markup);
+ gtk_misc_set_alignment(GTK_MISC(header), 0.0, 0.5);
+ gtk_table_attach(
+ GTK_TABLE(distro_table), header,
+ 0, 1, table_count, table_count + 1,
+ GTK_FILL, GTK_FILL, 0, 0
+ );
+ table_count++;
+ }
+
+ /* hardware section */
+
+ markup = g_strdup_printf(_("<b>Hardware</b>"));
+ hardware_table = add_section(GTK_BOX(vbox), markup, data->processors.size(), 2, NULL);
+ g_free(markup);
+
+ markup = procman::format_size(data->memory_bytes);
+ memory_label = add_row(GTK_TABLE(hardware_table), _("Memory:"),
+ markup, 0);
+ g_free(markup);
+
+ for (guint i = 0; i < data->processors.size(); ++i) {
+ const gchar * t;
+ if (data->processors.size() > 1) {
+ markup = g_strdup_printf(_("Processor %d:"), i);
+ t = markup;
+ }
+ else {
+ markup = NULL;
+ t = _("Processor:");
+ }
+
+ processor_label = add_row(GTK_TABLE(hardware_table), t,
+ data->processors[i].c_str(), 1 + i);
+
+ if(markup)
+ g_free(markup);
+ }
+
+ /* disk space section */
+
+ markup = g_strdup_printf(_("<b>System Status</b>"));
+ disk_space_table = add_section(GTK_BOX(vbox), markup, 1, 2, NULL);
+ g_free(markup);
+
+ markup = procman::format_size(data->free_space_bytes);
+ disk_space_label = add_row(GTK_TABLE(disk_space_table),
+ _("Available disk space:"), markup,
+ 0);
+ g_free(markup);
+
+ return hbox;
+}
+
+
+
+namespace procman
+{
+ void build_sysinfo_ui()
+ {
+ static GtkWidget* ui;
+
+ if (!ui) {
+ ProcData* procdata = ProcData::get_instance();
+ ui = procman_create_sysinfo_view();
+ GtkBox* box = GTK_BOX(gtk_notebook_get_nth_page(GTK_NOTEBOOK(procdata->notebook),
+ PROCMAN_TAB_SYSINFO));
+ gtk_box_pack_start(box, ui, TRUE, TRUE, 0);
+ gtk_widget_show_all(ui);
+ }
+ }
+}
diff --git a/src/sysinfo.h b/src/sysinfo.h
new file mode 100644
index 0000000..9ed9c1d
--- /dev/null
+++ b/src/sysinfo.h
@@ -0,0 +1,9 @@
+#ifndef H_MATE_SYSTEM_MONITOR_SYSINFO_H_1155594649
+#define H_MATE_SYSTEM_MONITOR_SYSINFO_H_1155594649
+
+namespace procman
+{
+ void build_sysinfo_ui();
+}
+
+#endif /* H_MATE_SYSTEM_MONITOR_SYSINFO_H_1155594649 */
diff --git a/src/util.cpp b/src/util.cpp
new file mode 100644
index 0000000..907bf80
--- /dev/null
+++ b/src/util.cpp
@@ -0,0 +1,510 @@
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstring.h>
+#include <gtk/gtk.h>
+
+#include <glibtop/proctime.h>
+#include <glibtop/procstate.h>
+#include <unistd.h>
+
+#include <stddef.h>
+#include <cstring>
+
+#include "util.h"
+#include "procman.h"
+
+extern "C" {
+#include "e_date.h"
+}
+
+
+static const char*
+format_process_state(guint state)
+{
+ const char *status;
+
+ switch (state)
+ {
+ case GLIBTOP_PROCESS_RUNNING:
+ status = _("Running");
+ break;
+
+ case GLIBTOP_PROCESS_STOPPED:
+ status = _("Stopped");
+ break;
+
+ case GLIBTOP_PROCESS_ZOMBIE:
+ status = _("Zombie");
+ break;
+
+ case GLIBTOP_PROCESS_UNINTERRUPTIBLE:
+ status = _("Uninterruptible");
+ break;
+
+ default:
+ status = _("Sleeping");
+ break;
+ }
+
+ return status;
+}
+
+
+
+static char *
+mnemonic_safe_process_name(const char *process_name)
+{
+ const char *p;
+ GString *name;
+
+ name = g_string_new ("");
+
+ for(p = process_name; *p; ++p)
+ {
+ g_string_append_c (name, *p);
+
+ if(*p == '_')
+ g_string_append_c (name, '_');
+ }
+
+ return g_string_free (name, FALSE);
+}
+
+
+
+static inline unsigned divide(unsigned *q, unsigned *r, unsigned d)
+{
+ *q = *r / d;
+ *r = *r % d;
+ return *q != 0;
+}
+
+
+/*
+ * @param d: duration in centiseconds
+ * @type d: unsigned
+ */
+static char *
+format_duration_for_display(unsigned centiseconds)
+{
+ unsigned weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0;
+
+ (void)(divide(&seconds, &centiseconds, 100)
+ && divide(&minutes, &seconds, 60)
+ && divide(&hours, &minutes, 60)
+ && divide(&days, &hours, 24)
+ && divide(&weeks, &days, 7));
+
+ if (weeks)
+ /* xgettext: weeks, days */
+ return g_strdup_printf(_("%uw%ud"), weeks, days);
+
+ if (days)
+ /* xgettext: days, hours (0 -> 23) */
+ return g_strdup_printf(_("%ud%02uh"), days, hours);
+
+ if (hours)
+ /* xgettext: hours (0 -> 23), minutes, seconds */
+ return g_strdup_printf(_("%u:%02u:%02u"), hours, minutes, seconds);
+
+ /* xgettext: minutes, seconds, centiseconds */
+ return g_strdup_printf(_("%u:%02u.%02u"), minutes, seconds, centiseconds);
+}
+
+
+
+GtkWidget*
+procman_make_label_for_mmaps_or_ofiles(const char *format,
+ const char *process_name,
+ unsigned pid)
+{
+ GtkWidget *label;
+ char *name, *title;
+
+ name = mnemonic_safe_process_name (process_name);
+ title = g_strdup_printf(format, name, pid);
+ label = gtk_label_new_with_mnemonic (title);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0f, 0.5f);
+
+ g_free (title);
+ g_free (name);
+
+ return label;
+}
+
+
+
+/**
+ * procman::format_size:
+ * @size:
+ *
+ * Formats the file size passed in @bytes in a way that is easy for
+ * the user to read. Gives the size in bytes, kibibytes, mebibytes or
+ * gibibytes, choosing whatever is appropriate.
+ *
+ * Returns: a newly allocated string with the size ready to be shown.
+ **/
+
+gchar*
+procman::format_size(guint64 size, guint64 max_size, bool want_bits)
+{
+ enum {
+ K_INDEX,
+ M_INDEX,
+ G_INDEX
+ };
+
+ struct Format {
+ guint64 factor;
+ const char* string;
+ };
+
+ const Format all_formats[2][3] = {
+ { { 1UL << 10, N_("%.1f KiB") },
+ { 1UL << 20, N_("%.1f MiB") },
+ { 1UL << 30, N_("%.1f GiB") } },
+ { { 1000, N_("%.1f kbit") },
+ { 1000000, N_("%.1f Mbit") },
+ { 1000000000, N_("%.1f Gbit") } }
+ };
+
+ const Format (&formats)[3] = all_formats[want_bits ? 1 : 0];
+
+ if (want_bits) {
+ size *= 8;
+ max_size *= 8;
+ }
+
+ if (max_size == 0)
+ max_size = size;
+
+ if (max_size < formats[K_INDEX].factor) {
+ const char *format = (want_bits
+ ? dngettext(GETTEXT_PACKAGE, "%u bit", "%u bits", (guint) size)
+ : dngettext(GETTEXT_PACKAGE, "%u byte", "%u bytes",(guint) size));
+ return g_strdup_printf (format, (guint) size);
+ } else {
+ guint64 factor;
+ const char* format = NULL;
+
+ if (max_size < formats[M_INDEX].factor) {
+ factor = formats[K_INDEX].factor;
+ format = formats[K_INDEX].string;
+ } else if (max_size < formats[G_INDEX].factor) {
+ factor = formats[M_INDEX].factor;
+ format = formats[M_INDEX].string;
+ } else {
+ factor = formats[G_INDEX].factor;
+ format = formats[G_INDEX].string;
+ }
+
+ return g_strdup_printf(_(format), size / (double)factor);
+ }
+}
+
+
+
+gboolean
+load_symbols(const char *module, ...)
+{
+ GModule *mod;
+ gboolean found_all = TRUE;
+ va_list args;
+
+ mod = g_module_open(module, static_cast<GModuleFlags>(G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL));
+
+ if (!mod)
+ return FALSE;
+
+ procman_debug("Found %s", module);
+
+ va_start(args, module);
+
+ while (1) {
+ const char *name;
+ void **symbol;
+
+ name = va_arg(args, char*);
+
+ if (!name)
+ break;
+
+ symbol = va_arg(args, void**);
+
+ if (g_module_symbol(mod, name, symbol)) {
+ procman_debug("Loaded %s from %s", name, module);
+ }
+ else {
+ procman_debug("Could not load %s from %s", name, module);
+ found_all = FALSE;
+ break;
+ }
+ }
+
+ va_end(args);
+
+
+ if (found_all)
+ g_module_make_resident(mod);
+ else
+ g_module_close(mod);
+
+ return found_all;
+}
+
+
+static gboolean
+is_debug_enabled(void)
+{
+ static gboolean init;
+ static gboolean enabled;
+
+ if (!init) {
+ enabled = g_getenv("MATE_SYSTEM_MONITOR_DEBUG") != NULL;
+ init = TRUE;
+ }
+
+ return enabled;
+}
+
+
+static double get_relative_time(void)
+{
+ static unsigned long start_time;
+ GTimeVal tv;
+
+ if (G_UNLIKELY(!start_time)) {
+ glibtop_proc_time buf;
+ glibtop_get_proc_time(&buf, getpid());
+ start_time = buf.start_time;
+ }
+
+ g_get_current_time(&tv);
+ return (tv.tv_sec - start_time) + 1e-6 * tv.tv_usec;
+}
+
+
+void
+procman_debug_real(const char *file, int line, const char *func,
+ const char *format, ...)
+{
+ va_list args;
+ char *msg;
+
+ if (G_LIKELY(!is_debug_enabled()))
+ return;
+
+ va_start(args, format);
+ msg = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ g_debug("[%.3f %s:%d %s] %s", get_relative_time(), file, line, func, msg);
+
+ g_free(msg);
+}
+
+
+
+namespace procman
+{
+ void size_cell_data_func(GtkTreeViewColumn *, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data)
+ {
+ const guint index = GPOINTER_TO_UINT(user_data);
+
+ guint64 size;
+ GValue value = { 0 };
+
+ gtk_tree_model_get_value(model, iter, index, &value);
+
+ switch (G_VALUE_TYPE(&value)) {
+ case G_TYPE_ULONG:
+ size = g_value_get_ulong(&value);
+ break;
+
+ case G_TYPE_UINT64:
+ size = g_value_get_uint64(&value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ g_value_unset(&value);
+
+ char *str = procman::format_size(size);
+ g_object_set(renderer, "text", str, NULL);
+ g_free(str);
+ }
+
+
+ /*
+ Same as above but handles size == 0 as not available
+ */
+ void size_na_cell_data_func(GtkTreeViewColumn *, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data)
+ {
+ const guint index = GPOINTER_TO_UINT(user_data);
+
+ guint64 size;
+ GValue value = { 0 };
+
+ gtk_tree_model_get_value(model, iter, index, &value);
+
+ switch (G_VALUE_TYPE(&value)) {
+ case G_TYPE_ULONG:
+ size = g_value_get_ulong(&value);
+ break;
+
+ case G_TYPE_UINT64:
+ size = g_value_get_uint64(&value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ g_value_unset(&value);
+
+ if (size == 0)
+ g_object_set(renderer, "markup", _("<i>N/A</i>"), NULL);
+ else {
+ char *str = procman::format_size(size);
+ g_object_set(renderer, "text", str, NULL);
+ g_free(str);
+ }
+
+ }
+
+
+ void duration_cell_data_func(GtkTreeViewColumn *, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data)
+ {
+ const guint index = GPOINTER_TO_UINT(user_data);
+
+ unsigned time;
+ GValue value = { 0 };
+
+ gtk_tree_model_get_value(model, iter, index, &value);
+
+ switch (G_VALUE_TYPE(&value)) {
+ case G_TYPE_ULONG:
+ time = g_value_get_ulong(&value);
+ break;
+
+ case G_TYPE_UINT64:
+ time = g_value_get_uint64(&value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ g_value_unset(&value);
+
+ time = 100 * time / ProcData::get_instance()->frequency;
+ char *str = format_duration_for_display(time);
+ g_object_set(renderer, "text", str, NULL);
+ g_free(str);
+ }
+
+
+ void time_cell_data_func(GtkTreeViewColumn *, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data)
+ {
+ const guint index = GPOINTER_TO_UINT(user_data);
+
+ time_t time;
+ GValue value = { 0 };
+
+ gtk_tree_model_get_value(model, iter, index, &value);
+
+ switch (G_VALUE_TYPE(&value)) {
+ case G_TYPE_ULONG:
+ time = g_value_get_ulong(&value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ g_value_unset(&value);
+
+ char *str = procman_format_date_for_display(time);
+ g_object_set(renderer, "text", str, NULL);
+ g_free(str);
+ }
+
+ void status_cell_data_func(GtkTreeViewColumn *, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data)
+ {
+ const guint index = GPOINTER_TO_UINT(user_data);
+
+ guint state;
+ GValue value = { 0 };
+
+ gtk_tree_model_get_value(model, iter, index, &value);
+
+ switch (G_VALUE_TYPE(&value)) {
+ case G_TYPE_UINT:
+ state = g_value_get_uint(&value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ g_value_unset(&value);
+
+ const char *str = format_process_state(state);
+ g_object_set(renderer, "text", str, NULL);
+ }
+
+
+ template<>
+ void tree_store_update<const char>(GtkTreeModel* model, GtkTreeIter* iter, int column, const char* new_value)
+ {
+ char* current_value;
+
+ gtk_tree_model_get(model, iter, column, &current_value, -1);
+
+ if (!current_value or std::strcmp(current_value, new_value) != 0)
+ gtk_tree_store_set(GTK_TREE_STORE(model), iter, column, new_value, -1);
+
+ g_free(current_value);
+ }
+
+
+
+
+ std::string format_rate(guint64 rate, guint64 max_rate, bool want_bits)
+ {
+ char* bytes = procman::format_size(rate, max_rate, want_bits);
+ // xgettext: rate, 10MiB/s or 10Mbit/s
+ std::string formatted_rate(make_string(g_strdup_printf(_("%s/s"), bytes)));
+ g_free(bytes);
+ return formatted_rate;
+ }
+
+
+ std::string format_network(guint64 rate, guint64 max_rate)
+ {
+ return procman::format_size(rate, max_rate, ProcData::get_instance()->config.network_in_bits);
+ }
+
+
+ std::string format_network_rate(guint64 rate, guint64 max_rate)
+ {
+ return procman::format_rate(rate, max_rate, ProcData::get_instance()->config.network_in_bits);
+ }
+
+}
+
+
+
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..1a15b4b
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,151 @@
+// -*- mode: c++ -*-
+
+#ifndef H_MATE_SYSTEM_MONITOR_UTIL_1123178725
+#define H_MATE_SYSTEM_MONITOR_UTIL_1123178725
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <stddef.h>
+#include <cstring>
+#include <string>
+#include <functional>
+#include <algorithm>
+
+using std::string;
+
+template<typename T>
+inline int procman_cmp(T x, T y)
+{
+ if (x == y)
+ return 0;
+
+ if (x < y)
+ return -1;
+
+ return 1;
+}
+
+#define PROCMAN_CMP(X, Y) procman_cmp((X), (Y))
+#define PROCMAN_RCMP(X, Y) procman_cmp((Y), (X));
+
+GtkWidget*
+procman_make_label_for_mmaps_or_ofiles(const char *format,
+ const char *process_name,
+ unsigned pid);
+
+gboolean
+load_symbols(const char *module, ...) G_GNUC_NULL_TERMINATED;
+
+
+void
+procman_debug_real(const char *file, int line, const char *func,
+ const char *format, ...) G_GNUC_PRINTF(4, 5);
+
+#define procman_debug(FMT, ...) procman_debug_real(__FILE__, __LINE__, __func__, FMT, ##__VA_ARGS__)
+
+inline string make_string(char *c_str)
+{
+ if (!c_str) {
+ procman_debug("NULL string");
+ return string();
+ }
+
+ string s(c_str);
+ g_free(c_str);
+ return s;
+}
+
+
+
+
+template<typename Map>
+class UnrefMapValues
+ : public std::unary_function<void, Map>
+{
+public:
+ void operator()(const typename Map::value_type &it) const
+ {
+ if (it.second)
+ g_object_unref(it.second);
+ }
+};
+
+
+template<typename Map>
+inline void unref_map_values(Map &map)
+{
+ std::for_each(map.begin(), map.end(), UnrefMapValues<Map>());
+}
+
+
+namespace procman
+{
+ void size_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data);
+
+ void size_na_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data);
+
+ void duration_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data);
+
+ void time_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data);
+
+ void status_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer user_data);
+
+ template<typename T>
+ void poison(T &t, char c)
+ {
+ memset(&t, c, sizeof t);
+ }
+
+
+
+ //
+ // Stuff to update a tree_store in a smart way
+ //
+
+ template<typename T>
+ void tree_store_update(GtkTreeModel* model, GtkTreeIter* iter, int column, const T& new_value)
+ {
+ T current_value;
+
+ gtk_tree_model_get(model, iter, column, &current_value, -1);
+
+ if (current_value != new_value)
+ gtk_tree_store_set(GTK_TREE_STORE(model), iter, column, new_value, -1);
+ }
+
+ // undefined
+ // catch every thing about pointers
+ // just to make sure i'm not doing anything wrong
+ template<typename T>
+ void tree_store_update(GtkTreeModel* model, GtkTreeIter* iter, int column, T* new_value);
+
+ // specialized versions for strings
+ template<>
+ void tree_store_update<const char>(GtkTreeModel* model, GtkTreeIter* iter, int column, const char* new_value);
+
+ template<>
+ inline void tree_store_update<char>(GtkTreeModel* model, GtkTreeIter* iter, int column, char* new_value)
+ {
+ tree_store_update<const char>(model, iter, column, new_value);
+ }
+
+ gchar* format_size(guint64 size, guint64 max = 0, bool want_bits = false);
+
+ std::string format_rate(guint64 rate, guint64 max_rate = 0, bool want_bits = false);
+
+ std::string format_network(guint64 rate, guint64 max_rate = 0);
+ std::string format_network_rate(guint64 rate, guint64 max_rate = 0);
+}
+
+
+#endif /* H_MATE_SYSTEM_MONITOR_UTIL_1123178725 */