From d75b80df07aa6d62971e8aa115145d7f94df3a17 Mon Sep 17 00:00:00 2001
From: Danny Auble <da@llnl.gov>
Date: Tue, 29 Aug 2006 21:38:30 +0000
Subject: [PATCH] admin mode works for partitions Availablity and Nodes

---
 src/sview/block_info.c |  49 +++++++++---
 src/sview/common.c     | 153 +++++++++++++++++++++++++++++++----
 src/sview/job_info.c   | 100 ++++++++++++++++-------
 src/sview/node_info.c  |  88 ++++++++++++++++++---
 src/sview/part_info.c  | 176 ++++++++++++++++++++++++++++++++---------
 src/sview/sview.c      |  52 +++++++-----
 src/sview/sview.h      |  38 ++++++++-
 7 files changed, 528 insertions(+), 128 deletions(-)

diff --git a/src/sview/block_info.c b/src/sview/block_info.c
index 16557e38cb4..22554a42063 100644
--- a/src/sview/block_info.c
+++ b/src/sview/block_info.c
@@ -67,20 +67,31 @@ enum {
 };
 
 static display_data_t display_data_block[] = {
-	{G_TYPE_INT, SORTID_POS, NULL, FALSE, -1, refresh_block},
+	{G_TYPE_INT, SORTID_POS, NULL, FALSE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
 	{G_TYPE_STRING, SORTID_PARTITION, "Partition", 
-	 TRUE, -1, refresh_block},
+	 TRUE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
 	{G_TYPE_STRING, SORTID_BLOCK, "Bluegene Block", 
-	 TRUE, -1, refresh_block},
-	{G_TYPE_STRING, SORTID_STATE, "State", TRUE, -1, refresh_block},
-	{G_TYPE_STRING, SORTID_USER, "User", TRUE, -1, refresh_block},
+	 TRUE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
+	{G_TYPE_STRING, SORTID_STATE, "State", TRUE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
+	{G_TYPE_STRING, SORTID_USER, "User", TRUE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
 	{G_TYPE_STRING, SORTID_CONN, "Connection Type", 
-	 TRUE, -1, refresh_block},
-	{G_TYPE_STRING, SORTID_USE, "Node Use", TRUE, -1, refresh_block},
-	{G_TYPE_STRING, SORTID_NODES, "Nodes", TRUE, -1, refresh_block},
-	{G_TYPE_STRING, SORTID_NODELIST, "BP List", TRUE, -1, refresh_block},
-	{G_TYPE_POINTER, SORTID_POINTER, NULL, FALSE, -1, refresh_block},
-	{G_TYPE_INT, SORTID_UPDATED, NULL, FALSE, -1, refresh_block},
+	 TRUE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
+	{G_TYPE_STRING, SORTID_USE, "Node Use", TRUE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
+	{G_TYPE_STRING, SORTID_NODES, "Nodes", TRUE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
+	{G_TYPE_STRING, SORTID_NODELIST, "BP List", TRUE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
+	{G_TYPE_POINTER, SORTID_POINTER, NULL, FALSE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
+	{G_TYPE_INT, SORTID_UPDATED, NULL, FALSE, -1, refresh_block,
+	 create_model_block, admin_edit_block},
 	{G_TYPE_NONE, -1, NULL, FALSE, -1}
 };
 
@@ -738,6 +749,22 @@ extern int get_new_info_node_select(node_select_info_msg_t **node_select_ptr,
 	return error_code;
 }
 
+extern GtkListStore *create_model_block(int type)
+{
+	GtkListStore *model = NULL;
+	
+	return model;
+}
+
+extern void admin_edit_block(GtkCellRendererText *cell,
+			     const char *path_string,
+			     const char *new_text,
+			     gpointer data)
+{
+	g_print("Something block related altered\n");
+	g_static_mutex_unlock(&sview_mutex);
+}
+
 extern void get_info_block(GtkTable *table, display_data_t *display_data)
 {
 	int part_error_code = SLURM_SUCCESS;
diff --git a/src/sview/common.c b/src/sview/common.c
index f2ce2467090..4dce6401813 100644
--- a/src/sview/common.c
+++ b/src/sview/common.c
@@ -26,6 +26,7 @@
 \*****************************************************************************/
 
 #include "src/sview/sview.h"
+#include "src/common/parse_time.h"
 
 typedef struct {
 	GtkTreeModel *model;
@@ -104,13 +105,48 @@ static int _sort_iter_compare_func_int(GtkTreeModel *model,
 	return ret;
 }
 
+static void _editing_started(GtkCellRenderer *cell,
+			     GtkCellEditable *editable,
+			     const gchar     *path,
+			     gpointer         data)
+{
+	g_static_mutex_lock(&sview_mutex);
+}
+
+static void _editing_canceled(GtkCellRenderer *cell,
+			       gpointer         data)
+{
+	g_static_mutex_unlock(&sview_mutex);
+
+}
+
 static void _add_col_to_treeview(GtkTreeView *tree_view, 
 				 display_data_t *display_data)
 {
-	GtkTreeViewColumn   *col;
-	GtkCellRenderer     *renderer;
-	renderer = gtk_cell_renderer_text_new();
-	col = gtk_tree_view_column_new();	
+	GtkTreeViewColumn *col = gtk_tree_view_column_new();
+	GtkCellRenderer *renderer = gtk_cell_renderer_combo_new();
+	GtkListStore *model = (display_data->create_model)(display_data->id);
+		
+	if(model) {
+		g_object_set(renderer,
+			     "model", model,
+			     "text-column", 0,
+			     "has-entry", FALSE,
+			     "editable", TRUE,
+			     NULL);
+
+		g_signal_connect(renderer, "editing-started",
+				 G_CALLBACK(_editing_started), NULL);
+		g_signal_connect(renderer, "editing-canceled",
+				 G_CALLBACK(_editing_canceled), NULL);
+  		g_signal_connect(renderer, "edited",
+				 G_CALLBACK(display_data->admin_edit), 
+				 gtk_tree_view_get_model(tree_view));
+		
+		g_object_set_data(G_OBJECT(renderer), "column", 
+				  GINT_TO_POINTER(display_data->id));
+	}
+
 	gtk_tree_view_column_pack_start(col, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(col, renderer, 
 					   "text", display_data->id);
@@ -429,9 +465,9 @@ extern GtkTreeStore *create_treestore(GtkTreeView *tree_view,
 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(treestore), 
 					     1, 
 					     GTK_SORT_ASCENDING);
-
-	load_header(tree_view, display_data);
+	
 	gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(treestore));
+	load_header(tree_view, display_data);
 	g_object_unref(GTK_TREE_MODEL(treestore));
 
 	return treestore;
@@ -455,34 +491,36 @@ extern void right_button_pressed(GtkTreeView *tree_view,
 	}
 }
 
-extern void row_clicked(GtkTreeView *tree_view, GdkEventButton *event, 
+extern gboolean row_clicked(GtkTreeView *tree_view, GdkEventButton *event, 
 			const display_data_t *display_data)
 {
 	GtkTreePath *path = NULL;
 	GtkTreeSelection *selection = NULL;
-	
-        if(!gtk_tree_view_get_path_at_pos(tree_view,
+	gboolean did_something = FALSE;
+	/*  right click? */
+	if(!gtk_tree_view_get_path_at_pos(tree_view,
 					  (gint) event->x, 
 					  (gint) event->y,
 					  &path, NULL, NULL, NULL)) {
-		return;
+		return did_something;
 	}
 	selection = gtk_tree_view_get_selection(tree_view);
 	gtk_tree_selection_unselect_all(selection);
 	gtk_tree_selection_select_path(selection, path);
-			 	
-	/* expand/collapse row 
-	   or right mouse button
-	   or double click?
-	*/
+	
 	if(event->x <= 20) {	
 		if(!gtk_tree_view_expand_row(tree_view, path, FALSE))
 			gtk_tree_view_collapse_row(tree_view, path);
+		did_something = TRUE;
 	} else if(event->button == 3) {
 		right_button_pressed(tree_view, path, event, 
 				     display_data, ROW_CLICKED);
-	}
+		did_something = TRUE;
+	} else if(!admin_mode)
+		did_something = TRUE;
 	gtk_tree_path_free(path);
+	
+	return did_something;
 }
 
 extern popup_info_t *create_popup_info(int type, int dest_type, char *title)
@@ -749,3 +787,86 @@ extern GtkWidget *create_pulldown_combo(display_data_t *display_data,
 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
 	return combo;
 }
+
+/*
+ * str_tolower - convert string to all lower case
+ * upper_str IN - upper case input string
+ * RET - lower case version of upper_str, caller must be xfree
+ */ 
+extern char *str_tolower(char *upper_str)
+{
+	int i = strlen(upper_str) + 1;
+	char *lower_str = xmalloc(i);
+
+	for (i=0; upper_str[i]; i++)
+		lower_str[i] = tolower((int) upper_str[i]);
+
+	return lower_str;
+}
+
+extern char *get_reason()
+{
+	char *reason_str = NULL;
+	int len = 0;
+	GtkWidget *table = gtk_table_new(1, 2, FALSE);
+	GtkWidget *label = gtk_label_new("Reason ");
+	GtkWidget *entry = gtk_entry_new();
+	GtkWidget *popup = gtk_dialog_new_with_buttons(
+		"State change Reason",
+		GTK_WINDOW(main_window),
+		GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_OK,
+		GTK_RESPONSE_OK,
+		GTK_STOCK_CANCEL,
+		GTK_RESPONSE_CANCEL,
+		NULL);
+	//GError *error = NULL;
+	int response = 0;
+	char *user_name = NULL;
+	char time_buf[64], time_str[32];
+	time_t now = time(NULL);
+			
+	gtk_container_set_border_width(GTK_CONTAINER(table), 10);
+	
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(popup)->vbox), 
+			   table, FALSE, FALSE, 0);
+	
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);	
+	gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 2, 0, 1);
+	
+	gtk_widget_show_all(popup);
+	response = gtk_dialog_run (GTK_DIALOG(popup));
+
+	if (response == GTK_RESPONSE_OK)
+	{
+		reason_str = xstrdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+		len = strlen(reason_str) - 1;
+		/* Append user, date and time */
+		xstrcat(reason_str, " [");
+		user_name = getlogin();
+		if (user_name)
+			xstrcat(reason_str, user_name);
+		else {
+			sprintf(time_buf, "%d", getuid());
+			xstrcat(reason_str, time_buf);
+		}
+		slurm_make_time_str(&now, time_str, sizeof(time_str));
+		snprintf(time_buf, sizeof(time_buf), "@%s]", time_str); 
+		xstrcat(reason_str, time_buf);
+		/* maybe later we will update the status bar */
+		/* temp = g_strdup_printf("Refresh Interval set to %d seconds.", */
+/* 				       global_sleep_time); */
+/* 		gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), 1, */
+/* 				   temp); */
+/* 		g_free(temp); */
+/* 		if (!g_thread_create(_refresh_thr, NULL, FALSE, &error)) */
+/* 		{ */
+/* 			g_printerr ("Failed to create refresh thread: %s\n",  */
+/* 				    error->message); */
+/* 		} */
+	}
+
+	gtk_widget_destroy(popup);	
+	
+	return reason_str;
+}
diff --git a/src/sview/job_info.c b/src/sview/job_info.c
index 141c3ecbf24..d1250656c76 100644
--- a/src/sview/job_info.c
+++ b/src/sview/job_info.c
@@ -65,49 +65,77 @@ enum {
 };
 
 static display_data_t display_data_job[] = {
-	{G_TYPE_INT, SORTID_POS, NULL, FALSE, -1, refresh_job},
-	{G_TYPE_INT, SORTID_JOBID, "JobID", TRUE, -1, refresh_job},
-	{G_TYPE_INT, SORTID_ALLOC, NULL, FALSE, -1, refresh_job},
-	{G_TYPE_STRING, SORTID_PARTITION, "Partition", TRUE, -1, refresh_job},
+	{G_TYPE_INT, SORTID_POS, NULL, FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_INT, SORTID_JOBID, "JobID", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_INT, SORTID_ALLOC, NULL, FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_STRING, SORTID_PARTITION, "Partition", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 #ifdef HAVE_BG
-	{G_TYPE_STRING, SORTID_BLOCK, "BG Block", TRUE, -1, refresh_job},
+	{G_TYPE_STRING, SORTID_BLOCK, "BG Block", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 #endif
-	{G_TYPE_STRING, SORTID_USER, "User", TRUE, -1, refresh_job},
-	{G_TYPE_STRING, SORTID_NAME, "Name", TRUE, -1, refresh_job},
-	{G_TYPE_STRING, SORTID_STATE, "State", TRUE, -1, refresh_job},
-	{G_TYPE_STRING, SORTID_TIME, "Running Time", TRUE, -1, refresh_job},
-	{G_TYPE_STRING, SORTID_NODES, "Nodes", TRUE, -1, refresh_job},
+	{G_TYPE_STRING, SORTID_USER, "User", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_STRING, SORTID_NAME, "Name", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_STRING, SORTID_STATE, "State", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_STRING, SORTID_TIME, "Running Time", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_STRING, SORTID_NODES, "Nodes", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 #ifdef HAVE_BG
-	{G_TYPE_STRING, SORTID_NODELIST, "BP List", TRUE, -1, refresh_job},
+	{G_TYPE_STRING, SORTID_NODELIST, "BP List", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_REQ_NODELIST, "Requested BP List",
-	 FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_EXC_NODELIST, "Excluded BP List",
-	 FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 #else
-	{G_TYPE_STRING, SORTID_NODELIST, "Nodelist", TRUE, -1, refresh_job},
+	{G_TYPE_STRING, SORTID_NODELIST, "Nodelist", TRUE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_REQ_NODELIST, "Requested NodeList", 
-	 FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_EXC_NODELIST, "Excluded NodeList", 
-	 FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 #endif
-	{G_TYPE_STRING, SORTID_SUBMIT, "Submit Time", FALSE, -1, refresh_job},
-	{G_TYPE_STRING, SORTID_START, "Start Time", FALSE, -1, refresh_job},
-	{G_TYPE_STRING, SORTID_END, "End Time", FALSE, -1, refresh_job},
+	{G_TYPE_STRING, SORTID_SUBMIT, "Submit Time", FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_STRING, SORTID_START, "Start Time", FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_STRING, SORTID_END, "End Time", FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_SUSPEND, "Suspend Time", 
-	 FALSE, -1, refresh_job},
-	{G_TYPE_INT, SORTID_PRIORITY, "Priority", FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_INT, SORTID_PRIORITY, "Priority", FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_NUM_PROCS, "Num Processors", 
-	 FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_TASKS, "Num Tasks", 
-	 FALSE, -1, refresh_job},
-	{G_TYPE_INT, SORTID_SHARED, "Shared", FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
+	{G_TYPE_INT, SORTID_SHARED, "Shared", FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_CPUS_PER_TASK, "Cpus per Task", 
-	 FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_ACCOUNT, "Account Charged", 
-	 FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_STRING, SORTID_REASON, "Wait Reason", 
-	 FALSE, -1, refresh_job},	
-	{G_TYPE_INT, SORTID_UPDATED, NULL, FALSE, -1, refresh_job},
+	 FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},	
+	{G_TYPE_INT, SORTID_UPDATED, NULL, FALSE, -1, refresh_job,
+	 create_model_job, admin_edit_job},
 	{G_TYPE_NONE, -1, NULL, FALSE, -1}
 };
 
@@ -727,6 +755,22 @@ extern int get_new_info_job_step(job_step_info_response_msg_t **info_ptr,
 	return error_code;
 }
 
+extern GtkListStore *create_model_job(int type)
+{
+	GtkListStore *model = NULL;
+	
+	return model;
+}
+
+extern void admin_edit_job(GtkCellRendererText *cell,
+			   const char *path_string,
+			   const char *new_text,
+			   gpointer data)
+{
+	g_print("Something job related altered\n");
+	g_static_mutex_unlock(&sview_mutex);
+}
+
 extern void get_info_job(GtkTable *table, display_data_t *display_data)
 {
 	int job_error_code = SLURM_SUCCESS;
diff --git a/src/sview/node_info.c b/src/sview/node_info.c
index 7681ddf67c3..1cb8751dca2 100644
--- a/src/sview/node_info.c
+++ b/src/sview/node_info.c
@@ -46,16 +46,26 @@ enum {
 };
 
 static display_data_t display_data_node[] = {
-	{G_TYPE_INT, SORTID_POS, NULL, FALSE, -1, refresh_node},
-	{G_TYPE_STRING, SORTID_NAME, "Name", TRUE, -1, refresh_node},
-	{G_TYPE_STRING, SORTID_STATE, "State", TRUE, -1, refresh_node},
-	{G_TYPE_INT, SORTID_CPUS, "CPU Count", TRUE, -1, refresh_node},
-	{G_TYPE_STRING, SORTID_MEMORY, "Real Memory", TRUE, -1, refresh_node},
-	{G_TYPE_STRING, SORTID_DISK, "Tmp Disk", TRUE, -1, refresh_node},
-	{G_TYPE_INT, SORTID_WEIGHT,"Weight", FALSE, -1, refresh_node},
-	{G_TYPE_STRING, SORTID_FEATURES, "Features", FALSE, -1, refresh_node},
-	{G_TYPE_STRING, SORTID_REASON, "Reason", FALSE, -1, refresh_node},
-	{G_TYPE_INT, SORTID_UPDATED, NULL, FALSE, -1, refresh_node},
+	{G_TYPE_INT, SORTID_POS, NULL, FALSE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_STRING, SORTID_NAME, "Name", TRUE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_STRING, SORTID_STATE, "State", TRUE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_INT, SORTID_CPUS, "CPU Count", TRUE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_STRING, SORTID_MEMORY, "Real Memory", TRUE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_STRING, SORTID_DISK, "Tmp Disk", TRUE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_INT, SORTID_WEIGHT,"Weight", FALSE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_STRING, SORTID_FEATURES, "Features", FALSE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_STRING, SORTID_REASON, "Reason", FALSE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
+	{G_TYPE_INT, SORTID_UPDATED, NULL, FALSE, -1, refresh_node,
+	 create_model_node, admin_edit_node},
 	{G_TYPE_NONE, -1, NULL, FALSE, -1}
 };
 
@@ -292,6 +302,64 @@ extern int get_new_info_node(node_info_msg_t **info_ptr, int force)
 	return error_code;
 }
 
+extern int update_state_node(GtkTreeStore *treestore, GtkTreeIter *iter, 
+			     int text_column, int num_column,
+			     const char *new_text,
+			     update_node_msg_t *node_msg)
+{
+	uint16_t state = (uint16_t) NO_VAL;
+	char *upper = NULL, *lower = NULL;		     
+	int i = 0;
+	int rc = SLURM_SUCCESS;
+
+	node_msg->reason = NULL;
+
+	if(!strcmp("drain", new_text)) {
+		state = NODE_STATE_DRAIN;
+		node_msg->reason = get_reason();
+	} else if(!strcmp("NoResp", new_text)) {
+		state = NODE_STATE_NO_RESPOND;
+	} else if(!strcmp("resume", new_text)) {
+		state = NODE_RESUME;
+	} else {
+		for(i = 0; i < NODE_STATE_END; i++) {
+			upper = node_state_string(i);
+			lower = str_tolower(upper);
+			if(!strcmp(upper, "?"))
+				break;
+			if(!strcmp(lower, new_text)) {
+				state = i;
+				xfree(lower);
+				break;
+			}
+			xfree(lower);
+		}
+	}
+	node_msg->node_state = (uint16_t)state;
+	
+	rc = slurm_update_node(node_msg);
+	xfree(node_msg->reason);
+	gtk_tree_store_set(treestore, iter, text_column, new_text,
+			   num_column,	state, -1);
+	return rc;
+}
+			
+extern GtkListStore *create_model_node(int type)
+{
+	GtkListStore *model = NULL;
+	
+	return model;
+}
+
+extern void admin_edit_node(GtkCellRendererText *cell,
+			    const char *path_string,
+			    const char *new_text,
+			    gpointer data)
+{
+	g_print("Something node related altered\n");
+	g_static_mutex_unlock(&sview_mutex);
+}
+
 extern void get_info_node(GtkTable *table, display_data_t *display_data)
 {
 	int error_code = SLURM_SUCCESS;
diff --git a/src/sview/part_info.c b/src/sview/part_info.c
index dfdcde7e862..c190726f158 100644
--- a/src/sview/part_info.c
+++ b/src/sview/part_info.c
@@ -85,28 +85,45 @@ enum {
 
 static display_data_t display_data_part[] = {
 	{G_TYPE_INT, SORTID_POS, NULL, FALSE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_NAME, "Partition", TRUE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_DEFAULT, "Default", TRUE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_AVAIL, "Availablity", TRUE, -1, refresh_part},
+	{G_TYPE_STRING, SORTID_NAME, "Partition", TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_DEFAULT, "Default", TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_AVAIL, "Availablity", TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
 	{G_TYPE_STRING, SORTID_TIMELIMIT, "Time Limit", 
-	 TRUE, -1, refresh_part},
+	 TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
 	{G_TYPE_STRING, SORTID_JOB_SIZE, "Job Size", FALSE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_ROOT, "Root", FALSE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_SHARE, "Share", FALSE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_GROUPS, "Groups", FALSE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_NODES, "Nodes", TRUE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_CPUS, "CPUs", FALSE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_DISK, "Temp Disk", FALSE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_MEM, "MEM", FALSE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_STATE, "State", TRUE, -1, refresh_part},
-	{G_TYPE_STRING, SORTID_WEIGHT, "Weight", TRUE, -1, refresh_part},
+	{G_TYPE_STRING, SORTID_ROOT, "Root", FALSE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_SHARE, "Share", FALSE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_GROUPS, "Groups", FALSE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_NODES, "Nodes", TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_CPUS, "CPUs", FALSE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_DISK, "Temp Disk", FALSE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_MEM, "MEM", FALSE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_STATE, "State", TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_STRING, SORTID_WEIGHT, "Weight", TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
 #ifdef HAVE_BG
-	{G_TYPE_STRING, SORTID_NODELIST, "BP List", TRUE, -1, refresh_part},
+	{G_TYPE_STRING, SORTID_NODELIST, "BP List", TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
 #else
-	{G_TYPE_STRING, SORTID_NODELIST, "NodeList", TRUE, -1, refresh_part},
+	{G_TYPE_STRING, SORTID_NODELIST, "NodeList", TRUE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
 #endif
-	{G_TYPE_INT, SORTID_STATE_NUM, NULL, FALSE, -1, refresh_part},
-	{G_TYPE_INT, SORTID_UPDATED, NULL, FALSE, -1, refresh_part},
+	{G_TYPE_INT, SORTID_STATE_NUM, NULL, FALSE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
+	{G_TYPE_INT, SORTID_UPDATED, NULL, FALSE, -1, refresh_part,
+	 create_model_part, admin_edit_part},
 
 	{G_TYPE_NONE, -1, NULL, FALSE, -1}
 };
@@ -155,22 +172,6 @@ _build_min_max_string(char *buffer, int buf_size, int min, int max, bool range)
 		return snprintf(buffer, buf_size, "%s+", tmp_min);
 }
 
-/*
- * _str_tolower - convert string to all lower case
- * upper_str IN - upper case input string
- * RET - lower case version of upper_str, caller must be xfree
- */ 
-static char *_str_tolower(char *upper_str)
-{
-	int i = strlen(upper_str) + 1;
-	char *lower_str = xmalloc(i);
-
-	for (i=0; upper_str[i]; i++)
-		lower_str[i] = tolower((int) upper_str[i]);
-
-	return lower_str;
-}
-
 static void _subdivide_part(sview_part_info_t *sview_part_info,
 			    GtkTreeModel *model, 
 			    GtkTreeIter *sub_iter,
@@ -349,8 +350,8 @@ static void _update_part_sub_record(sview_part_sub_t *sview_part_sub,
 	else
 		gtk_tree_store_set(treestore, iter, SORTID_AVAIL, "down", -1);
 		
-	upper = node_state_string_compact(sview_part_sub->node_state);
-	lower = _str_tolower(upper);
+	upper = node_state_string(sview_part_sub->node_state);
+	lower = str_tolower(upper);
 	gtk_tree_store_set(treestore, iter, SORTID_STATE, 
 			   lower, -1);
 	xfree(lower);
@@ -605,8 +606,8 @@ static void _update_sview_part_sub(sview_part_sub_t *sview_part_sub,
 	
 	if (sview_part_sub->node_cnt == 0) {	/* first node added */
 		sview_part_sub->node_state = node_ptr->node_state;
-		sview_part_sub->features   = node_ptr->features;
-		sview_part_sub->reason     = node_ptr->reason;
+		sview_part_sub->features   = xstrdup(node_ptr->features);
+		sview_part_sub->reason     = xstrdup(node_ptr->reason);
 		sview_part_sub->min_cpus   = node_ptr->cpus;
 		sview_part_sub->max_cpus   = node_ptr->cpus;
 		sview_part_sub->min_disk   = node_ptr->tmp_disk;
@@ -688,8 +689,8 @@ static sview_part_sub_t *_create_sview_part_sub(partition_info_t *part_ptr,
 	sview_part_sub_ptr->min_weight = node_ptr->weight;
 	sview_part_sub_ptr->max_weight = node_ptr->weight;
 
-	sview_part_sub_ptr->features = node_ptr->features;
-	sview_part_sub_ptr->reason   = node_ptr->reason;
+	sview_part_sub_ptr->features = xstrdup(node_ptr->features);
+	sview_part_sub_ptr->reason   = xstrdup(node_ptr->reason);
 
 	sview_part_sub_ptr->hl = hostlist_create(node_ptr->name);
 	
@@ -879,6 +880,103 @@ extern int get_new_info_part(partition_info_msg_t **part_ptr, int force)
 	return error_code;
 }
 
+extern GtkListStore *create_model_part(int type)
+{
+	GtkListStore *model = NULL;
+	GtkTreeIter iter;
+	char *upper = NULL, *lower = NULL;		     
+	int i=0;
+	switch(type) {
+	case SORTID_AVAIL:
+		model = gtk_list_store_new(2, G_TYPE_STRING,
+					   G_TYPE_INT);
+		gtk_list_store_append(model, &iter);
+		gtk_list_store_set(model, &iter,
+				   0, "up",
+				   1, i,
+				   -1);	
+		gtk_list_store_append(model, &iter);
+		gtk_list_store_set(model, &iter,
+				   0, "down",
+				   1, i,
+				   -1);	
+		break;
+	case SORTID_STATE:
+		model = gtk_list_store_new(2, G_TYPE_STRING,
+					   G_TYPE_INT);
+		gtk_list_store_append(model, &iter);
+		gtk_list_store_set(model, &iter,
+				   0, "drain",
+				   1, i,
+				   -1);	
+		gtk_list_store_append(model, &iter);
+		gtk_list_store_set(model, &iter,
+				   0, "NoResp",
+				   1, i,
+				   -1);	
+		gtk_list_store_append(model, &iter);
+		gtk_list_store_set(model, &iter,
+				   0, "resume",
+				   1, i,
+				   -1);	
+		for(i = 0; i < NODE_STATE_END; i++) {
+			upper = node_state_string(i);
+			gtk_list_store_append(model, &iter);
+			lower = str_tolower(upper);
+			gtk_list_store_set(model, &iter,
+					   0, lower,
+					   1, i,
+					   -1);
+			xfree(lower);
+		}
+						
+		break;
+
+	}
+	return model;
+}
+
+extern void admin_edit_part(GtkCellRendererText *cell,
+			    const char *path_string,
+			    const char *new_text,
+			    gpointer data)
+{
+	GtkTreeStore *treestore = GTK_TREE_STORE(data);
+	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
+	GtkTreeIter iter;
+	update_node_msg_t node_msg;
+	update_part_msg_t part_msg;
+
+	int column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), 
+						       "column"));
+	gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore), &iter, path);
+	switch(column) {
+	case SORTID_AVAIL:
+		slurm_init_part_desc_msg(&part_msg);
+		gtk_tree_model_get(GTK_TREE_MODEL(treestore), &iter, 
+				   SORTID_NAME, 
+				   &part_msg.name, -1);
+		if (!strcasecmp(new_text, "up"))
+			part_msg.state_up = 1;
+		else
+			part_msg.state_up = 0;
+		slurm_update_partition(&part_msg);	
+		break;
+	case SORTID_STATE:
+		gtk_tree_model_get(GTK_TREE_MODEL(treestore), &iter, 
+				   SORTID_NODELIST, 
+				   &node_msg.node_names, -1);
+		update_state_node(treestore, &iter, 
+				  SORTID_STATE, SORTID_STATE_NUM,
+				  new_text, &node_msg);
+		g_free(node_msg.node_names);
+		break;			
+	}
+	gtk_tree_path_free (path);
+	
+	g_static_mutex_unlock(&sview_mutex);
+}
+
 extern void get_info_part(GtkTable *table, display_data_t *display_data)
 {
 	int part_error_code = SLURM_SUCCESS;
diff --git a/src/sview/sview.c b/src/sview/sview.c
index d6b635c50bd..5191076187a 100644
--- a/src/sview/sview.c
+++ b/src/sview/sview.c
@@ -45,37 +45,46 @@ int global_sleep_time = 5;
 bool admin_mode = FALSE;
 GtkWidget *main_notebook = NULL;
 GtkWidget *main_statusbar = NULL;
+GtkWidget *main_window = NULL;
+GStaticMutex sview_mutex = G_STATIC_MUTEX_INIT;
 
 display_data_t main_display_data[] = {
 	{G_TYPE_NONE, JOB_PAGE, "Jobs", TRUE, -1,
-	 refresh_main, get_info_job, specific_info_job, 
+	 refresh_main, create_model_job, admin_edit_job,
+	 get_info_job, specific_info_job, 
 	 set_menus_job, NULL},
 	{G_TYPE_NONE, STEP_PAGE, NULL, FALSE, -1,
-	 refresh_main, NULL,
+	 refresh_main, NULL, NULL, NULL,
 	 NULL, NULL, NULL},
 	{G_TYPE_NONE, PART_PAGE, "Partitions", TRUE, -1, 
-	 refresh_main, get_info_part, specific_info_part, 
+	 refresh_main, create_model_part, admin_edit_part,
+	 get_info_part, specific_info_part, 
 	 set_menus_part, NULL},
 #ifdef HAVE_BG
 	{G_TYPE_NONE, BLOCK_PAGE, "BG Blocks", TRUE, -1,
-	 refresh_main, get_info_block, specific_info_block, 
+	 refresh_main, NULL, NULL,
+	 get_info_block, specific_info_block, 
 	 set_menus_block, NULL},
 	{G_TYPE_NONE, NODE_PAGE, "Base Partitions", FALSE, -1,
-	 refresh_main, get_info_node, specific_info_node, 
+	 refresh_main, NULL, NULL,
+	 get_info_node, specific_info_node, 
 	 set_menus_node, NULL},
 #else
 	{G_TYPE_NONE, BLOCK_PAGE, "BG Blocks", FALSE, -1,
-	 refresh_main, get_info_block, specific_info_block, 
+	 refresh_main, NULL, NULL,
+	 get_info_block, specific_info_block, 
 	 set_menus_block, NULL},
 	{G_TYPE_NONE, NODE_PAGE, "Nodes", FALSE, -1,
-	 refresh_main, get_info_node, specific_info_node, 
+	 refresh_main, NULL, NULL,
+	 get_info_node, specific_info_node, 
 	 set_menus_node, NULL},
 #endif
 	{G_TYPE_NONE, SUBMIT_PAGE, "Submit Job", FALSE, -1,
-	 refresh_main, NULL, 
+	 refresh_main, NULL, NULL, NULL,
 	 NULL, NULL, NULL},
 	{G_TYPE_NONE, INFO_PAGE, NULL, FALSE, -1,
 	 refresh_main, NULL, NULL,
+	 NULL, NULL,
 	 NULL, NULL},
 	{G_TYPE_NONE, -1, NULL, FALSE, -1}
 };
@@ -89,10 +98,12 @@ void *_page_thr(void *arg)
 	xfree(page);
 
 	while(page_running[num]) {
+		g_static_mutex_lock(&sview_mutex);
 		gdk_threads_enter();
 		(display_data->get_info)(table, display_data);
 		gdk_flush();
 		gdk_threads_leave();
+		g_static_mutex_unlock(&sview_mutex);
 		sleep(global_sleep_time);
 	}	
 		
@@ -464,7 +475,6 @@ void _search_entry(GtkEntry *entry, GtkComboBox *combo)
 
 int main(int argc, char *argv[])
 {
-	GtkWidget *window = NULL;
 	GtkWidget *menubar = NULL;
 	GtkWidget *table = NULL;
 	GtkWidget *label = NULL;
@@ -492,13 +502,13 @@ int main(int argc, char *argv[])
 	gtk_init (&argc, &argv);
 	/* fill in all static info for pages */
 	/* Make a window */
-	window = gtk_dialog_new();
-	g_signal_connect(G_OBJECT(window), "delete_event",
+	main_window = gtk_dialog_new();
+	g_signal_connect(G_OBJECT(main_window), "delete_event",
 			 G_CALLBACK(_delete), NULL);
-	gtk_window_set_title(GTK_WINDOW(window), "Sview");
-	gtk_window_set_default_size(GTK_WINDOW(window), 600, 400);
-	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(window)->vbox),
-				       1);
+	gtk_window_set_title(GTK_WINDOW(main_window), "Sview");
+	gtk_window_set_default_size(GTK_WINDOW(main_window), 600, 400);
+	gtk_container_set_border_width(
+		GTK_CONTAINER(GTK_DIALOG(main_window)->vbox), 1);
 	/* Create the main notebook, place the position of the tabs */
 	main_notebook = gtk_notebook_new();
 	g_signal_connect(G_OBJECT(main_notebook), "switch_page",
@@ -508,7 +518,7 @@ int main(int argc, char *argv[])
 	gtk_table_set_homogeneous(GTK_TABLE(table), FALSE);
 	gtk_container_set_border_width(GTK_CONTAINER(table), 1);	
 	/* Create a menu */
-	menubar = _get_menubar_menu(window, main_notebook);
+	menubar = _get_menubar_menu(main_window, main_notebook);
 	gtk_table_attach_defaults(GTK_TABLE(table), menubar, 0, 1, 0, 1);
 
 	label = gtk_label_new("Search ");
@@ -521,7 +531,7 @@ int main(int argc, char *argv[])
 			 GTK_SHRINK, GTK_EXPAND | GTK_FILL,
 			 0, 0);
 	
-	entry = gtk_entry_new ();
+	entry = gtk_entry_new();
 	gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 0, 1,
 			 GTK_SHRINK, GTK_EXPAND | GTK_FILL,
 			 0, 0);
@@ -538,11 +548,11 @@ int main(int argc, char *argv[])
 	gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(main_statusbar), 
 					  FALSE);
 	/* Pack it all together */
-	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(main_window)->vbox),
 			   table, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), 
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(main_window)->vbox), 
 			   main_notebook, TRUE, TRUE, 0);	
-	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(main_window)->vbox),
 			   main_statusbar, FALSE, FALSE, 0);	
 	
 	for(i=0; i<PAGE_CNT; i++) {
@@ -556,7 +566,7 @@ int main(int argc, char *argv[])
 	/* tell signal we are done adding */
 	adding = 0;
 	popup_list = list_create(destroy_popup_info);
-	gtk_widget_show_all (window);
+	gtk_widget_show_all (main_window);
 
 	/* Finished! */
 	gtk_main ();
diff --git a/src/sview/sview.h b/src/sview/sview.h
index a8c307921b8..be2e5ee5898 100644
--- a/src/sview/sview.h
+++ b/src/sview/sview.h
@@ -126,6 +126,11 @@ struct display_data {
 	bool show;
 	int extra;
 	void (*refresh)     (GtkAction *action, gpointer user_data);
+	GtkListStore *(*create_model)(int type);
+	void (*admin_edit)  (GtkCellRendererText *cell,
+			     const char *path_string,
+			     const char *new_text,
+			     gpointer data);
 	void (*get_info)    (GtkTable *table, display_data_t *display_data);
 	void (*specific)    (popup_info_t *popup_win);
 	void (*set_menu)    (void *arg, GtkTreePath *path,
@@ -171,7 +176,8 @@ extern bool force_refresh;
 extern List popup_list;
 extern int global_sleep_time;
 extern bool admin_mode;
-	
+extern GtkWidget *main_window;
+extern GStaticMutex sview_mutex;	
 
 extern void init_grid(node_info_msg_t *node_info_ptr);
 extern int set_grid(int start, int end, int count);
@@ -189,6 +195,11 @@ extern void tab_pressed(GtkWidget *widget, GdkEventButton *event,
 
 // part_info.c
 extern void refresh_part(GtkAction *action, gpointer user_data);
+extern GtkListStore *create_model_part(int type);
+extern void admin_edit_part(GtkCellRendererText *cell,
+			    const char *path_string,
+			    const char *new_text,
+			    gpointer data);
 extern int get_new_info_part(partition_info_msg_t **part_ptr, int force);
 extern void get_info_part(GtkTable *table, display_data_t *display_data);
 extern void specific_info_part(popup_info_t *popup_win);
@@ -198,6 +209,11 @@ extern void popup_all_part(GtkTreeModel *model, GtkTreeIter *iter, int id);
 
 // block_info.c
 extern void refresh_block(GtkAction *action, gpointer user_data);
+extern GtkListStore *create_model_block(int type);
+extern void admin_edit_block(GtkCellRendererText *cell,
+			     const char *path_string,
+			     const char *new_text,
+			     gpointer data);
 extern int get_new_info_node_select(node_select_info_msg_t **node_select_ptr,
 				    int force);
 extern void get_info_block(GtkTable *table, display_data_t *display_data);
@@ -208,6 +224,11 @@ extern void popup_all_block(GtkTreeModel *model, GtkTreeIter *iter, int id);
 
 // job_info.c
 extern void refresh_job(GtkAction *action, gpointer user_data);
+extern GtkListStore *create_model_job(int type);
+extern void admin_edit_job(GtkCellRendererText *cell,
+			   const char *path_string,
+			   const char *new_text,
+			   gpointer data);
 extern int get_new_info_job(job_info_msg_t **info_ptr, int force);
 extern int get_new_info_job_step(job_step_info_response_msg_t **info_ptr, 
 				 int force);
@@ -219,6 +240,15 @@ extern void popup_all_job(GtkTreeModel *model, GtkTreeIter *iter, int id);
 
 // node_info.c
 extern void refresh_node(GtkAction *action, gpointer user_data);
+extern int update_state_node(GtkTreeStore *treestore, GtkTreeIter *iter, 
+			     int text_column, int num_column,
+			     const char *new_text,
+			     update_node_msg_t *node_msg);
+extern GtkListStore *create_model_node(int type);
+extern void admin_edit_node(GtkCellRendererText *cell,
+			    const char *path_string,
+			    const char *new_text,
+			    gpointer data);
 extern int get_new_info_node(node_info_msg_t **info_ptr, int force);
 extern void get_info_node(GtkTable *table, display_data_t *display_data);
 extern void specific_info_node(popup_info_t *popup_win);
@@ -247,8 +277,8 @@ extern void right_button_pressed(GtkTreeView *tree_view, GtkTreePath *path,
 				 GdkEventButton *event, 
 				 const display_data_t *display_data,
 				 int type);
-extern void row_clicked(GtkTreeView *tree_view, GdkEventButton *event, 
-			const display_data_t *display_data);
+extern gboolean row_clicked(GtkTreeView *tree_view, GdkEventButton *event, 
+			    const display_data_t *display_data);
 extern popup_info_t *create_popup_info(int type, int dest_type, char *title);
 extern void setup_popup_info(popup_info_t *popup_win, 
 			     display_data_t *display_data, 
@@ -262,4 +292,6 @@ extern void *popup_thr(popup_info_t *popup_win);
 extern void remove_old(GtkTreeModel *model, int updated);
 extern GtkWidget *create_pulldown_combo(display_data_t *display_data,
 					int count);
+extern char *str_tolower(char *upper_str);
+extern char *get_reason();
 #endif
-- 
GitLab