| /*****************************************************************************\ |
| * common.c - common functions used by tabs in sview |
| ***************************************************************************** |
| * Copyright (C) 2004-2007 The Regents of the University of California. |
| * Copyright (C) 2008-2009 Lawrence Livermore National Security. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Danny Auble <da@llnl.gov> |
| * |
| * CODE-OCEC-09-009. All rights reserved. |
| * |
| * This file is part of Slurm, a resource management program. |
| * For details, see <https://slurm.schedmd.com/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * Slurm 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. |
| * |
| * Slurm 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 Slurm; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #include "config.h" |
| |
| #include "src/sview/sview.h" |
| #include "src/common/parse_time.h" |
| #include <gdk/gdkkeysyms.h> |
| |
| #define TOPO_DEBUG 0 |
| #define _DEBUG 0 |
| static bool menu_right_pressed = false; |
| |
| typedef struct { |
| display_data_t *display_data; |
| void (*pfunc)(GtkTreeModel*, GtkTreeIter*, int); |
| GtkTreeView *tree_view; |
| } each_t; |
| |
| typedef struct { |
| GtkTreeIter iter; |
| GtkTreeModel *model; |
| GtkTreeView *treeview; |
| } treedata_t; |
| |
| static gboolean control_key_in_effect = false; |
| static gboolean enter_key_in_effect = false; |
| |
| typedef struct topoinfo_tree { |
| uint32_t record_count; |
| topo_info_t *topo_array; |
| } topoinfo_tree_t; |
| |
| static int _find_node_inx (char *name) |
| { |
| int i; |
| |
| if ((name == NULL) || (name[0] == '\0')) { |
| info("_find_node_inx passed NULL name"); |
| return -1; |
| } |
| |
| |
| for (i = 0; i < g_node_info_ptr->record_count; i++) { |
| if (g_node_info_ptr->node_array[i].name == NULL) |
| continue; /* Future node or other anomaly */ |
| if (!xstrcmp(name, g_node_info_ptr->node_array[i].name)) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| static void _display_topology(void) |
| { |
| slurm_print_topo_info_msg(stdout, g_topo_info_msg_ptr, NULL, NULL, 0); |
| } |
| |
| static void _foreach_popup_all(GtkTreeModel *model, |
| GtkTreePath *path, |
| GtkTreeIter *iter, |
| gpointer userdata) |
| { |
| |
| each_t *each = userdata; |
| each->pfunc(model, iter, each->display_data->id); |
| } |
| |
| static void _foreach_full_info(GtkTreeModel *model, |
| GtkTreePath *path, |
| GtkTreeIter *iter, |
| gpointer userdata) |
| { |
| |
| each_t *each = userdata; |
| (each->display_data->set_menu)(each->tree_view, NULL, path, |
| FULL_CLICKED); |
| popup_pos.x = popup_pos.slider + popup_pos.cntr * 10; |
| popup_pos.y = popup_pos.cntr * 22; |
| popup_pos.cntr++; |
| if (popup_pos.cntr > 10) { |
| popup_pos.cntr = 1; |
| popup_pos.slider += 100; |
| } |
| } |
| |
| /* These next 2 functions are here to make it so we don't magically |
| * click on something before we really want to in a menu. |
| */ |
| static gboolean _menu_button_pressed(GtkWidget *widget, GdkEventButton *event, |
| gpointer extra) |
| { |
| if (event->button == 3) { |
| menu_right_pressed = true; |
| return true; |
| } |
| return false; |
| } |
| |
| static gboolean _menu_button_released(GtkWidget *widget, GdkEventButton *event, |
| gpointer extra) |
| { |
| if (event->button == 3 && !menu_right_pressed) |
| return true; |
| menu_right_pressed = false; |
| return false; |
| } |
| |
| static gboolean _frame_callback(GtkWindow *window, |
| GdkEvent *event, gpointer data) |
| { |
| |
| if (event->expose.send_event == 0) { |
| default_sview_config.fi_popup_width = event->configure.width; |
| default_sview_config.fi_popup_height = event->configure.height; |
| working_sview_config.fi_popup_width = event->configure.width; |
| working_sview_config.fi_popup_height = event->configure.height; |
| |
| list_itr_t *itr = list_iterator_create(popup_list); |
| popup_info_t *popup_win = NULL; |
| |
| while ((popup_win = list_next(itr))) { |
| gtk_window_resize(GTK_WINDOW(popup_win->popup), |
| working_sview_config.fi_popup_width, |
| working_sview_config.fi_popup_height); |
| } |
| list_iterator_destroy(itr); |
| } |
| |
| |
| return false; |
| } |
| |
| static void _handle_response(GtkDialog *dialog, gint response_id, |
| popup_info_t *popup_win) |
| { |
| |
| switch(response_id) { |
| case GTK_RESPONSE_OK: //refresh |
| (popup_win->display_data->refresh)(NULL, popup_win); |
| break; |
| case GTK_RESPONSE_DELETE_EVENT: // exit |
| case GTK_RESPONSE_CLOSE: // close |
| delete_popup(NULL, NULL, popup_win->spec_info->title); |
| break; |
| case GTK_RESPONSE_CANCEL: // cancel |
| delete_popups(); |
| break; |
| default: |
| g_print("handle unknown response %d\n", response_id); |
| break; |
| } |
| return; |
| } |
| |
| static int _sort_iter_compare_func_char(GtkTreeModel *model, |
| GtkTreeIter *a, |
| GtkTreeIter *b, |
| gpointer userdata) |
| { |
| int sortcol = GPOINTER_TO_INT(userdata); |
| int ret = 0; |
| int len1 = 0, len2 = 0; |
| gchar *name1 = NULL, *name2 = NULL; |
| |
| gtk_tree_model_get(model, a, sortcol, &name1, -1); |
| gtk_tree_model_get(model, b, sortcol, &name2, -1); |
| |
| if (!name1 && !name2) |
| goto cleanup; /* both equal => ret = 0 */ |
| else if (!name1 || !name2) { |
| ret = (name1 == NULL) ? -1 : 1; |
| } else { |
| /* sort like a human would |
| meaning snowflake2 would be greater than |
| snowflake12 */ |
| len1 = strlen(name1); |
| len2 = strlen(name2); |
| while ((ret < len1) && (!g_ascii_isdigit(name1[ret]))) |
| ret++; |
| if (ret < len1) { |
| if (!g_ascii_strncasecmp(name1, name2, ret)) { |
| if (len1 > len2) |
| ret = 1; |
| else if (len1 < len2) |
| ret = -1; |
| else |
| ret = g_ascii_strcasecmp(name1, name2); |
| } else |
| ret = g_ascii_strcasecmp(name1, name2); |
| } else |
| ret = g_ascii_strcasecmp(name1, name2); |
| } |
| cleanup: |
| g_free(name1); |
| g_free(name2); |
| |
| return ret; |
| } |
| |
| static int _sort_iter_compare_func_int(GtkTreeModel *model, |
| GtkTreeIter *a, |
| GtkTreeIter *b, |
| gpointer userdata) |
| { |
| int sortcol = GPOINTER_TO_INT(userdata); |
| int ret = 0; |
| gint int1, int2; |
| |
| gtk_tree_model_get(model, a, sortcol, &int1, -1); |
| gtk_tree_model_get(model, b, sortcol, &int2, -1); |
| |
| if (int1 != int2) |
| ret = (int1 > int2) ? 1 : -1; |
| |
| return ret; |
| } |
| |
| static int _sort_iter_compare_func_nodes(GtkTreeModel *model, |
| GtkTreeIter *a, |
| GtkTreeIter *b, |
| gpointer userdata) |
| { |
| int sortcol = GPOINTER_TO_INT(userdata); |
| int ret = 0; |
| gchar *name1 = NULL, *name2 = NULL; |
| |
| gtk_tree_model_get(model, a, sortcol, &name1, -1); |
| gtk_tree_model_get(model, b, sortcol, &name2, -1); |
| |
| if (!name1 && !name2) |
| goto cleanup; /* both equal => ret = 0 */ |
| else if (!name1 || !name2) |
| ret = (name1 == NULL) ? -1 : 1; |
| else { |
| uint64_t int1=0, int2=0, tmp_int; |
| int spot=0; |
| /* If this is in a mixed state we need to get them all */ |
| while (name1[spot]) { |
| while (name1[spot] |
| && !g_ascii_isdigit(name1[spot])) { |
| spot++; |
| } |
| if (!name1[spot]) |
| break; |
| tmp_int = atoi(name1+spot); |
| while (name1[spot] && g_ascii_isdigit(name1[spot])) { |
| spot++; |
| } |
| |
| if (!name1[spot]) { |
| } else if (name1[spot] == 'K') |
| tmp_int *= 1024; |
| else if (name1[spot] == 'M') |
| tmp_int *= 1048576; |
| else if (name1[spot] == 'G') |
| tmp_int *= 1073741824; |
| |
| int1 += tmp_int; |
| } |
| |
| spot=0; |
| while (name2[spot]) { |
| while (name2[spot] |
| && !g_ascii_isdigit(name2[spot])) { |
| spot++; |
| } |
| if (!name2[spot]) |
| break; |
| tmp_int = atoi(name2+spot); |
| while (name2[spot] && g_ascii_isdigit(name2[spot])) { |
| spot++; |
| } |
| if (!name2[spot]) { |
| } else if (name2[spot] == 'K') |
| tmp_int *= 1024; |
| else if (name2[spot] == 'M') |
| tmp_int *= 1048576; |
| else if (name2[spot] == 'G') |
| tmp_int *= 1073741824; |
| |
| int2 += tmp_int; |
| } |
| |
| if (int1 != int2) |
| ret = (int1 > int2) ? 1 : -1; |
| } |
| cleanup: |
| g_free(name1); |
| g_free(name2); |
| |
| return ret; |
| } |
| |
| static void _editing_started(GtkCellRenderer *cell, |
| GtkCellEditable *editable, |
| const gchar *path, |
| gpointer data) |
| { |
| gdk_threads_leave(); |
| g_mutex_lock(sview_mutex); |
| } |
| |
| static void _editing_canceled(GtkCellRenderer *cell, |
| gpointer data) |
| { |
| g_mutex_unlock(sview_mutex); |
| } |
| |
| static void *_editing_thr(gpointer arg) |
| { |
| int msg_id = 0; |
| sleep(5); |
| gdk_threads_enter(); |
| msg_id = GPOINTER_TO_INT(arg); |
| gtk_statusbar_remove(GTK_STATUSBAR(main_statusbar), |
| STATUS_ADMIN_EDIT, msg_id); |
| //gdk_flush(); |
| gdk_threads_leave(); |
| return NULL; |
| } |
| |
| static void _cell_data_func(GtkTreeViewColumn *col, |
| GtkCellRenderer *renderer, |
| GtkTreeModel *model, |
| GtkTreeIter *iter, |
| gpointer data) |
| { |
| GdkPixbuf *pixbuf = NULL; |
| char *color_char, *color_char2; |
| uint32_t color; |
| |
| g_object_get(renderer, "pixbuf", &pixbuf, NULL); |
| if (!pixbuf) |
| return; |
| |
| gtk_tree_model_get(model, iter, |
| GPOINTER_TO_INT(g_object_get_data(G_OBJECT(renderer), |
| "column")), |
| &color_char, -1); |
| if (!color_char) |
| return; |
| |
| color_char2 = color_char+1; |
| color = strtoul(color_char2, (char **)&color_char2, 16); |
| g_free(color_char); |
| |
| /* we need to shift over 2 spots for the alpha */ |
| gdk_pixbuf_fill(pixbuf, color << 8); |
| /* This only has to be done once, but I can't find any way to |
| * set something to only make it happen once. It only takes |
| * 3-5 usecs to do it so I didn't worry about it doing it |
| * multiple times. If you can figure out how to make this |
| * happen only once please fix, but the pointers for the col, |
| * renderer, and pixbuf are all the same. You could put in |
| * some think in the tree_model, but that seemed a bit more |
| * cumbersome. - da |
| */ |
| } |
| |
| static void _add_col_to_treeview(GtkTreeView *tree_view, |
| display_data_t *display_data, int color_column) |
| { |
| GtkTreeViewColumn *col; |
| GtkListStore *model; |
| GtkCellRenderer *renderer = NULL; |
| |
| /* Since some systems have different default columns (some |
| * which aren't displayed on all types of clusters only add a |
| * column if there is a name for it. */ |
| if (!display_data->name && (display_data->extra != EDIT_COLOR)) |
| return; |
| |
| col = gtk_tree_view_column_new(); |
| model = (display_data->create_model)(display_data->id); |
| |
| if (model && display_data->extra != EDIT_NONE) { |
| renderer = gtk_cell_renderer_combo_new(); |
| g_object_set(renderer, |
| "model", model, |
| "text-column", 0, |
| "has-entry", 1, |
| "editable", true, |
| NULL); |
| } else if (display_data->extra == EDIT_TEXTBOX) { |
| renderer = gtk_cell_renderer_text_new(); |
| g_object_set(renderer, |
| "editable", true, |
| NULL); |
| } else if (display_data->extra == EDIT_COLOR) { |
| GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, |
| 8, 10, 20); |
| renderer = gtk_cell_renderer_pixbuf_new(); |
| g_object_set(renderer, "pixbuf", pixbuf, NULL); |
| g_object_unref(pixbuf); |
| } else |
| renderer = gtk_cell_renderer_text_new(); |
| |
| if (model) |
| g_object_unref(model); |
| |
| gtk_tree_view_column_pack_start(col, renderer, true); |
| |
| g_object_set_data(G_OBJECT(renderer), "column", |
| GINT_TO_POINTER(display_data->id)); |
| |
| if (display_data->extra == EDIT_COLOR) { |
| gtk_tree_view_column_set_cell_data_func( |
| col, renderer, _cell_data_func, |
| NULL, NULL); |
| } else { |
| 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)); |
| |
| gtk_tree_view_column_add_attribute(col, renderer, |
| "text", display_data->id); |
| gtk_tree_view_column_set_expand(col, true); |
| gtk_tree_view_column_set_reorderable(col, true); |
| gtk_tree_view_column_set_resizable(col, true); |
| gtk_tree_view_column_set_sort_column_id(col, display_data->id); |
| gtk_tree_view_column_set_title(col, display_data->name); |
| } |
| |
| gtk_tree_view_append_column(tree_view, col); |
| } |
| |
| static void _toggle_state_changed(GtkCheckMenuItem *menuitem, |
| display_data_t *display_data) |
| { |
| if (display_data->show) |
| display_data->show = false; |
| else |
| display_data->show = true; |
| toggled = true; |
| refresh_main(NULL, NULL); |
| } |
| |
| static void _popup_state_changed(GtkCheckMenuItem *menuitem, |
| display_data_t *display_data) |
| { |
| popup_info_t *popup_win = (popup_info_t *) display_data->user_data; |
| if (display_data->show) |
| display_data->show = false; |
| else |
| display_data->show = true; |
| popup_win->toggled = 1; |
| (display_data->refresh)(NULL, display_data->user_data); |
| } |
| |
| static void _selected_page(GtkMenuItem *menuitem, display_data_t *display_data) |
| { |
| treedata_t *treedata = (treedata_t *)display_data->user_data; |
| each_t each; |
| memset(&each, 0, sizeof(each_t)); |
| each.tree_view = treedata->treeview; |
| each.display_data = display_data; |
| global_row_count = gtk_tree_selection_count_selected_rows( |
| gtk_tree_view_get_selection(treedata->treeview)); |
| switch(display_data->extra & EXTRA_BASE) { |
| case PART_PAGE: |
| each.pfunc = &popup_all_part; |
| break; |
| case JOB_PAGE: |
| each.pfunc = &popup_all_job; |
| break; |
| case NODE_PAGE: |
| each.pfunc = &popup_all_node; |
| break; |
| case RESV_PAGE: |
| each.pfunc = &popup_all_resv; |
| break; |
| case BB_PAGE: |
| each.pfunc = &popup_all_bb; |
| break; |
| case ADMIN_PAGE: |
| switch(display_data->id) { |
| case JOB_PAGE: |
| admin_job(treedata->model, &treedata->iter, |
| display_data->name,treedata->treeview); |
| break; |
| case PART_PAGE: |
| select_admin_partitions(treedata->model, |
| &treedata->iter, |
| display_data, |
| treedata->treeview); |
| break; |
| case RESV_PAGE: |
| select_admin_resv(treedata->model, &treedata->iter, |
| display_data, treedata->treeview); |
| break; |
| case NODE_PAGE: |
| select_admin_nodes(treedata->model, &treedata->iter, |
| display_data, NO_VAL, |
| treedata->treeview); |
| break; |
| case BB_PAGE: |
| select_admin_bb(treedata->model, &treedata->iter, |
| display_data, treedata->treeview); |
| break; |
| default: |
| g_print("common admin got %d %d\n", |
| display_data->extra, |
| display_data->id); |
| } |
| break; |
| default: |
| g_print("common got %d %d\n", display_data->extra, |
| display_data->id); |
| } |
| if (each.pfunc) |
| gtk_tree_selection_selected_foreach( |
| gtk_tree_view_get_selection(treedata->treeview), |
| _foreach_popup_all, &each); |
| xfree(treedata); |
| } |
| |
| extern char * replspace (char *str) |
| { |
| int pntr = 0; |
| while (str[pntr]) { |
| if (str[pntr] == ' ') |
| str[pntr] = '_'; |
| pntr++; |
| } |
| return str; |
| } |
| |
| extern char * replus (char *str) |
| { |
| int pntr = 0; |
| while (str[pntr]) { |
| if (str[pntr] == '_') |
| str[pntr] = ' '; |
| pntr++; |
| } |
| return str; |
| } |
| |
| extern void free_switch_nodes_maps( |
| switch_record_bitmaps_t *sw_nodes_bitmaps_ptr) |
| { |
| while (sw_nodes_bitmaps_ptr++) { |
| if (!sw_nodes_bitmaps_ptr->node_bitmap) |
| break; |
| FREE_NULL_BITMAP(sw_nodes_bitmaps_ptr->node_bitmap); |
| } |
| g_switch_nodes_maps = NULL; |
| } |
| |
| extern int build_nodes_bitmap(char *node_names, bitstr_t **bitmap) |
| |
| { |
| char *this_node_name; |
| bitstr_t *my_bitmap; |
| hostlist_t *host_list; |
| int node_inx = -1; |
| |
| if (TOPO_DEBUG) |
| g_print("...............build_nodes_bitmap............%s\n", |
| node_names); |
| my_bitmap = bit_alloc(g_node_info_ptr->record_count); |
| *bitmap = my_bitmap; |
| |
| if (!node_names) { |
| error("build_nodes_bitmap: node_names is NULL"); |
| return EINVAL; |
| } |
| |
| if (!(host_list = hostlist_create(node_names))) { |
| error("build_nodes_bitmap: hostlist_create(%s) error", |
| node_names); |
| return EINVAL; |
| } |
| |
| /*spin hostlist and map nodes into a bitmap*/ |
| while ((this_node_name = hostlist_shift(host_list))) { |
| node_inx = _find_node_inx(this_node_name); |
| free(this_node_name); |
| |
| if (node_inx == -1) |
| continue; |
| |
| bit_set(my_bitmap, (bitoff_t)node_inx); |
| } |
| hostlist_destroy(host_list); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| extern int get_topo_conf(void) |
| { |
| int i; |
| switch_record_bitmaps_t sw_nodes_bitmaps; |
| switch_record_bitmaps_t *sw_nodes_bitmaps_ptr; |
| topoinfo_tree_t *topo_info; |
| |
| if (TOPO_DEBUG) |
| g_print("get_topo_conf\n"); |
| |
| if (!g_topo_info_msg_ptr && |
| slurm_load_topo(&g_topo_info_msg_ptr, NULL)) { |
| slurm_perror ("slurm_load_topo error"); |
| if (TOPO_DEBUG) |
| g_print("get_topo_conf error !!\n"); |
| return SLURM_ERROR; |
| } |
| |
| if (!g_topo_info_msg_ptr->topo_info) { |
| slurm_free_topo_info_msg(g_topo_info_msg_ptr); |
| g_topo_info_msg_ptr = NULL; |
| return SLURM_ERROR; |
| } |
| |
| if (g_topo_info_msg_ptr->topo_info->plugin_id != TOPOLOGY_PLUGIN_TREE) { |
| slurm_free_topo_info_msg(g_topo_info_msg_ptr); |
| g_topo_info_msg_ptr = NULL; |
| if (TOPO_DEBUG) |
| g_print("get_topo_conf only topology tree supported!!\n"); |
| return SLURM_ERROR; |
| } |
| |
| if (g_switch_nodes_maps) |
| free_switch_nodes_maps(g_switch_nodes_maps); |
| topo_info = g_topo_info_msg_ptr->topo_info->data; |
| |
| g_switch_nodes_maps = |
| xmalloc(sizeof(sw_nodes_bitmaps) * topo_info->record_count); |
| sw_nodes_bitmaps_ptr = g_switch_nodes_maps; |
| g_switch_nodes_maps_count = 0; |
| |
| if (TOPO_DEBUG) |
| g_print("_display_topology, record_count = %d\n", |
| topo_info->record_count); |
| for (i = 0; i < topo_info->record_count; i++) { |
| if (!topo_info->topo_array[i].nodes) |
| continue; |
| if (topo_info->topo_array[i].level) |
| continue; |
| if (TOPO_DEBUG) { |
| g_print("ptr->nodes = %s \n", |
| topo_info->topo_array[i].nodes); |
| } |
| if (build_nodes_bitmap( |
| topo_info->topo_array[g_switch_nodes_maps_count] |
| .nodes, |
| &sw_nodes_bitmaps_ptr->node_bitmap)) { |
| g_print("Invalid node name (%s) in switch %s\n", |
| topo_info->topo_array[g_switch_nodes_maps_count] |
| .nodes, |
| topo_info->topo_array[g_switch_nodes_maps_count] |
| .name); |
| } |
| sw_nodes_bitmaps_ptr++; |
| g_switch_nodes_maps_count++; |
| } |
| |
| if (TOPO_DEBUG) |
| _display_topology(); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| extern int get_row_number(GtkTreeView *tree_view, GtkTreePath *path) |
| { |
| GtkTreeModel *model = gtk_tree_view_get_model(tree_view); |
| GtkTreeIter iter; |
| int line = 0; |
| |
| if (!model) { |
| g_error("error getting the model from the tree_view"); |
| return -1; |
| } |
| |
| if (!gtk_tree_model_get_iter(model, &iter, path)) { |
| g_error("get row, error getting iter from model"); |
| return -1; |
| } |
| gtk_tree_model_get(model, &iter, POS_LOC, &line, -1); |
| return line; |
| } |
| |
| extern int find_col(display_data_t *display_data, int type) |
| { |
| int i = 0; |
| |
| while (display_data++) { |
| if (display_data->id == -1) |
| break; |
| if (display_data->id == type) |
| return i; |
| i++; |
| } |
| return -1; |
| } |
| |
| extern const char *find_col_name(display_data_t *display_data, int type) |
| { |
| while (display_data++) { |
| if (display_data->id == -1) |
| break; |
| if (display_data->id == type) |
| return display_data->name; |
| } |
| return NULL; |
| } |
| |
| extern void *get_pointer(GtkTreeView *tree_view, GtkTreePath *path, int loc) |
| { |
| GtkTreeModel *model = gtk_tree_view_get_model(tree_view); |
| GtkTreeIter iter; |
| void *ptr = NULL; |
| |
| if (!model) { |
| g_error("error getting the model from the tree_view"); |
| return ptr; |
| } |
| |
| if (!gtk_tree_model_get_iter(model, &iter, path)) { |
| g_error("get pointer, error getting iter from model"); |
| return ptr; |
| } |
| gtk_tree_model_get(model, &iter, loc, &ptr, -1); |
| return ptr; |
| } |
| |
| extern void make_fields_menu(popup_info_t *popup_win, GtkMenu *menu, |
| display_data_t *display_data, int count) |
| { |
| GtkWidget *menuitem = NULL; |
| display_data_t *first_display_data = display_data; |
| int i = 0; |
| |
| /* we don't want to display anything on the full info page */ |
| if (popup_win && popup_win->spec_info->type == INFO_PAGE) |
| return; |
| |
| g_signal_connect(G_OBJECT(menu), "button-press-event", |
| G_CALLBACK(_menu_button_pressed), |
| NULL); |
| g_signal_connect(G_OBJECT(menu), "button-release-event", |
| G_CALLBACK(_menu_button_released), |
| NULL); |
| |
| for(i=0; i<count; i++) { |
| while (display_data++) { |
| if (display_data->id == -1) |
| break; |
| if (!display_data->name) |
| continue; |
| if (display_data->id != i) |
| continue; |
| |
| menuitem = gtk_check_menu_item_new_with_label( |
| display_data->name); |
| |
| gtk_check_menu_item_set_active( |
| GTK_CHECK_MENU_ITEM(menuitem), |
| display_data->show); |
| if (popup_win) { |
| display_data->user_data = popup_win; |
| g_signal_connect( |
| menuitem, "toggled", |
| G_CALLBACK(_popup_state_changed), |
| display_data); |
| } else { |
| g_signal_connect( |
| menuitem, "toggled", |
| G_CALLBACK(_toggle_state_changed), |
| display_data); |
| } |
| gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); |
| break; |
| } |
| display_data = first_display_data; |
| } |
| } |
| |
| extern void set_page_opts(int page, display_data_t *display_data, |
| int count, char* initial_opts) |
| { |
| page_opts_t *page_opts; |
| list_itr_t *itr = NULL; |
| char *col_name = NULL; |
| |
| xassert(page < PAGE_CNT); |
| |
| page_opts = &working_sview_config.page_opts[page]; |
| if (!page_opts->col_list) { |
| page_opts->def_col_list = 1; |
| page_opts->col_list = list_create(xfree_ptr); |
| slurm_addto_char_list(page_opts->col_list, initial_opts); |
| } |
| |
| page_opts->display_data = display_data; |
| |
| itr = list_iterator_create(page_opts->col_list); |
| while ((col_name = list_next(itr))) { |
| replus(col_name); |
| while (display_data++) { |
| if (display_data->id == -1) |
| break; |
| if (!display_data->name) |
| continue; |
| if (!xstrncasecmp(col_name, display_data->name, |
| strlen(col_name))) { |
| display_data->show = true; |
| break; |
| } |
| } |
| display_data = page_opts->display_data; |
| } |
| list_iterator_destroy(itr); |
| } |
| |
| extern void make_options_menu(GtkTreeView *tree_view, GtkTreePath *path, |
| GtkMenu *menu, display_data_t *display_data) |
| { |
| GtkWidget *menuitem = NULL; |
| treedata_t *treedata = xmalloc(sizeof(treedata_t)); |
| treedata->model = gtk_tree_view_get_model(tree_view); |
| treedata->treeview = tree_view; |
| |
| g_signal_connect(G_OBJECT(menu), "button-press-event", |
| G_CALLBACK(_menu_button_pressed), |
| NULL); |
| g_signal_connect(G_OBJECT(menu), "button-release-event", |
| G_CALLBACK(_menu_button_released), |
| NULL); |
| |
| if (!gtk_tree_model_get_iter(treedata->model, &treedata->iter, path)) { |
| g_error("make menus error getting iter from model\n"); |
| return; |
| } |
| |
| /* check selection list */ |
| global_row_count = gtk_tree_selection_count_selected_rows( |
| gtk_tree_view_get_selection(tree_view)); |
| |
| if (display_data->user_data) |
| xfree(display_data->user_data); |
| |
| while (display_data++) { |
| if (display_data->id == -1) { |
| break; |
| } |
| |
| if (!display_data->name) |
| continue; |
| display_data->user_data = treedata; |
| menuitem = gtk_menu_item_new_with_label(display_data->name); |
| |
| g_signal_connect(menuitem, "activate", |
| G_CALLBACK(_selected_page), |
| display_data); |
| gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); |
| |
| } |
| } |
| |
| extern GtkScrolledWindow *create_scrolled_window(void) |
| { |
| GtkScrolledWindow *scrolled_window = NULL; |
| GtkWidget *table = NULL; |
| table = gtk_table_new(1, 1, false); |
| |
| gtk_container_set_border_width(GTK_CONTAINER(table), 10); |
| |
| scrolled_window = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new( |
| NULL, NULL)); |
| gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 10); |
| |
| gtk_scrolled_window_set_policy(scrolled_window, |
| GTK_POLICY_AUTOMATIC, |
| GTK_POLICY_AUTOMATIC); |
| |
| gtk_scrolled_window_add_with_viewport(scrolled_window, table); |
| |
| return scrolled_window; |
| } |
| |
| extern GtkWidget *create_entry(void) |
| { |
| GtkWidget *entry = gtk_entry_new(); |
| |
| gtk_entry_set_activates_default(GTK_ENTRY(entry), true); |
| |
| return entry; |
| } |
| |
| extern void create_page(GtkNotebook *notebook, display_data_t *display_data) |
| { |
| GtkScrolledWindow *scrolled_window = create_scrolled_window(); |
| GtkWidget *event_box = gtk_event_box_new(); |
| GtkWidget *label = gtk_label_new(display_data->name); |
| GtkWidget *close_button = gtk_event_box_new(); |
| GtkWidget *table; |
| GtkWidget *image = NULL; |
| int err; |
| |
| if (display_data->id == TAB_PAGE) { |
| table = gtk_table_new(PAGE_CNT, 3, false); |
| image = gtk_image_new_from_stock( |
| GTK_STOCK_ADD, GTK_ICON_SIZE_SMALL_TOOLBAR); |
| } else { |
| table = gtk_table_new(1, 3, false); |
| image = gtk_image_new_from_stock( |
| GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_SMALL_TOOLBAR); |
| g_signal_connect(G_OBJECT(close_button), "button-press-event", |
| G_CALLBACK(close_tab), |
| display_data); |
| } |
| |
| gtk_container_add(GTK_CONTAINER(close_button), image); |
| gtk_widget_set_size_request(close_button, 10, 10); |
| |
| //gtk_event_box_set_above_child(GTK_EVENT_BOX(close_button), false); |
| |
| gtk_container_add(GTK_CONTAINER(event_box), label); |
| gtk_event_box_set_above_child(GTK_EVENT_BOX(event_box), false); |
| g_signal_connect(G_OBJECT(event_box), "button-press-event", |
| G_CALLBACK(tab_pressed), |
| display_data); |
| |
| gtk_table_set_homogeneous(GTK_TABLE(table), false); |
| gtk_table_set_col_spacings(GTK_TABLE(table), 5); |
| gtk_container_set_border_width(GTK_CONTAINER(table), 1); |
| |
| gtk_table_attach_defaults(GTK_TABLE(table), event_box, 0, 1, 0, 1); |
| gtk_table_attach_defaults(GTK_TABLE(table), close_button, 2, 3, 0, 1); |
| gtk_container_set_focus_child(GTK_CONTAINER(table), label); |
| |
| gtk_widget_show_all(table); |
| //(display_data->set_fields)(GTK_MENU(menu)); |
| if ((err = gtk_notebook_append_page(GTK_NOTEBOOK(notebook), |
| GTK_WIDGET(scrolled_window), |
| table)) == -1) { |
| g_error("Couldn't add page to notebook\n"); |
| } |
| |
| display_data->extra = err; |
| |
| } |
| |
| extern GtkTreeView *create_treeview(display_data_t *local, list_t **button_list) |
| { |
| signal_params_t *signal_params = xmalloc(sizeof(signal_params_t)); |
| GtkTreeView *tree_view = GTK_TREE_VIEW(gtk_tree_view_new()); |
| local->user_data = NULL; |
| |
| signal_params->display_data = local; |
| signal_params->button_list = button_list; |
| if (working_sview_config.ruled_treeview) |
| gtk_tree_view_set_rules_hint (tree_view, true); |
| |
| g_signal_connect(G_OBJECT(tree_view), "button-press-event", |
| G_CALLBACK(row_clicked), |
| signal_params); |
| g_signal_connect(G_OBJECT(tree_view), "key_release_event", |
| G_CALLBACK(key_released), |
| signal_params); |
| g_signal_connect(G_OBJECT(tree_view), "key_press_event", |
| G_CALLBACK(key_pressed), |
| signal_params); |
| g_signal_connect(G_OBJECT(tree_view), "row-activated", |
| G_CALLBACK(row_activated), |
| signal_params); |
| gtk_widget_show(GTK_WIDGET(tree_view)); |
| list_push(signal_params_list, signal_params); |
| return tree_view; |
| |
| } |
| |
| extern GtkTreeView *create_treeview_2cols_attach_to_table(GtkTable *table) |
| { |
| GtkTreeView *tree_view = GTK_TREE_VIEW(gtk_tree_view_new()); |
| GtkTreeStore *treestore = |
| gtk_tree_store_new(3, G_TYPE_STRING, |
| G_TYPE_STRING, G_TYPE_STRING); |
| GtkTreeViewColumn *col = gtk_tree_view_column_new(); |
| GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); |
| |
| if (working_sview_config.ruled_treeview) |
| gtk_tree_view_set_rules_hint (tree_view, true); |
| |
| gtk_table_attach_defaults(table, |
| GTK_WIDGET(tree_view), |
| 0, 1, 0, 1); |
| |
| gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(treestore)); |
| |
| gtk_tree_view_column_pack_start(col, renderer, true); |
| gtk_tree_view_column_add_attribute(col, renderer, |
| "text", DISPLAY_NAME); |
| gtk_tree_view_column_add_attribute(col, renderer, |
| "font", DISPLAY_FONT); |
| gtk_tree_view_column_set_title(col, "Name"); |
| gtk_tree_view_column_set_resizable(col, true); |
| gtk_tree_view_column_set_expand(col, true); |
| gtk_tree_view_append_column(tree_view, col); |
| |
| col = gtk_tree_view_column_new(); |
| renderer = gtk_cell_renderer_text_new(); |
| gtk_tree_view_column_pack_start(col, renderer, true); |
| gtk_tree_view_column_add_attribute(col, renderer, |
| "text", DISPLAY_VALUE); |
| gtk_tree_view_column_add_attribute(col, renderer, |
| "font", DISPLAY_FONT); |
| gtk_tree_view_column_set_title(col, "Value"); |
| gtk_tree_view_column_set_resizable(col, true); |
| gtk_tree_view_column_set_expand(col, true); |
| gtk_tree_view_append_column(tree_view, col); |
| |
| col = gtk_tree_view_column_new(); |
| renderer = gtk_cell_renderer_text_new(); |
| gtk_tree_view_column_pack_start(col, renderer, true); |
| gtk_tree_view_column_set_visible(col, false); |
| gtk_tree_view_column_add_attribute(col, renderer, |
| "text", DISPLAY_FONT); |
| gtk_tree_view_append_column(tree_view, col); |
| |
| g_object_unref(treestore); |
| return tree_view; |
| } |
| |
| extern void create_treestore(GtkTreeView *tree_view, |
| display_data_t *display_data, int count, |
| int sort_column, int color_column) |
| { |
| GtkTreeStore *treestore = NULL; |
| GType types[count]; |
| int i=0; |
| |
| /*set up the types defined in the display_data_t */ |
| for(i=0; i<count; i++) { |
| types[display_data[i].id] = display_data[i].type; |
| } |
| |
| treestore = gtk_tree_store_newv(count, types); |
| if (!treestore) { |
| g_print("Can't create treestore.\n"); |
| return; |
| } |
| |
| gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(treestore)); |
| |
| for(i=1; i<count; i++) { |
| if (!display_data[i].show) |
| continue; |
| |
| _add_col_to_treeview(tree_view, &display_data[i], color_column); |
| if (!display_data[i].name) |
| continue; |
| |
| switch(display_data[i].type) { |
| case G_TYPE_INT: |
| gtk_tree_sortable_set_sort_func( |
| GTK_TREE_SORTABLE(treestore), |
| display_data[i].id, |
| _sort_iter_compare_func_int, |
| GINT_TO_POINTER(display_data[i].id), |
| NULL); |
| |
| break; |
| case G_TYPE_STRING: |
| if (!xstrcasecmp(display_data[i].name, "Node Count") |
| || !xstrcasecmp(display_data[i].name, "CPU Count") |
| || !xstrcasecmp(display_data[i].name, "Real Memory") |
| || !xstrcasecmp(display_data[i].name, "Port") |
| || !xstrcasecmp(display_data[i].name, "Tmp Disk")) { |
| gtk_tree_sortable_set_sort_func( |
| GTK_TREE_SORTABLE(treestore), |
| display_data[i].id, |
| _sort_iter_compare_func_nodes, |
| GINT_TO_POINTER(display_data[i].id), |
| NULL); |
| break; |
| } else { |
| gtk_tree_sortable_set_sort_func( |
| GTK_TREE_SORTABLE(treestore), |
| display_data[i].id, |
| _sort_iter_compare_func_char, |
| GINT_TO_POINTER(display_data[i].id), |
| NULL); |
| break; |
| } |
| default: |
| g_print("unknown type %d", |
| (int)display_data[i].type); |
| } |
| } |
| |
| if (sort_column >= 0) { |
| gtk_tree_sortable_set_sort_column_id( |
| GTK_TREE_SORTABLE(treestore), |
| sort_column, |
| GTK_SORT_ASCENDING); |
| } |
| |
| g_object_unref(treestore); |
| } |
| |
| extern gboolean right_button_pressed(GtkTreeView *tree_view, |
| GtkTreePath *path, |
| GdkEventButton *event, |
| const signal_params_t *signal_params, |
| int type) |
| { |
| GtkMenu *menu = GTK_MENU(gtk_menu_new()); |
| display_data_t *display_data = signal_params->display_data; |
| |
| if (type == ROW_CLICKED) { |
| if (_DEBUG) |
| g_print("right_button_pressed:global_row_count : %d\n", |
| global_row_count); |
| |
| /* These next 2 functions are there to keep the keyboard in |
| sync */ |
| if (!(event->state & GDK_CONTROL_MASK) |
| && (!(global_row_count > 0))) |
| gtk_tree_view_set_cursor(tree_view, path, NULL, false); |
| |
| gtk_widget_grab_focus(GTK_WIDGET(tree_view)); |
| |
| /* highlight the nodes from this row */ |
| (display_data->set_menu)(tree_view, *signal_params->button_list, |
| path, ROW_LEFT_CLICKED); |
| } |
| |
| (display_data->set_menu)(tree_view, menu, path, type); |
| gtk_widget_show_all(GTK_WIDGET(menu)); |
| gtk_menu_popup(menu, NULL, NULL, NULL, NULL, |
| event ? event->button : 0, |
| gdk_event_get_time((GdkEvent*)event)); |
| return true; |
| } |
| |
| extern gboolean left_button_pressed(GtkTreeView *tree_view, |
| GtkTreePath *path, |
| const signal_params_t *signal_params, |
| GdkEventButton *event) |
| { |
| static time_t last_time = 0; |
| time_t now = time(NULL); |
| gboolean rc = false; |
| GtkTreeIter iter; |
| GtkTreeModel *model = gtk_tree_view_get_model(tree_view); |
| display_data_t *display_data = signal_params->display_data; |
| static gpointer *last_user_data = NULL; |
| |
| /* These next 2 functions are there to keep the keyboard in |
| sync */ |
| if (!((event->state & GDK_CONTROL_MASK) |
| || (event->state & GDK_SHIFT_MASK))) |
| gtk_tree_view_set_cursor(tree_view, path, NULL, false); |
| |
| gtk_widget_grab_focus(GTK_WIDGET(tree_view)); /*give keyboard focus*/ |
| |
| if (signal_params->button_list) { |
| (display_data->set_menu)(tree_view, *signal_params->button_list, |
| path, ROW_LEFT_CLICKED); |
| } else |
| (display_data->set_menu)(tree_view, NULL, path, FULL_CLICKED); |
| |
| /* make sure it was a double click */ |
| if (!gtk_tree_model_get_iter(model, &iter, path)) { |
| g_error("left pressed, error getting iter from model\n"); |
| return rc; |
| } |
| if (!(now-last_time) |
| && (!last_user_data || (iter.user_data == last_user_data))) { |
| /* double click */ |
| (display_data->set_menu)(tree_view, NULL, path, FULL_CLICKED); |
| } |
| last_user_data = iter.user_data; |
| |
| if (!working_sview_config.admin_mode) |
| rc = true; |
| |
| last_time = now; |
| |
| return rc; |
| } |
| |
| extern gboolean row_activated(GtkTreeView *tree_view, GtkTreePath *path, |
| GtkTreeViewColumn *column, |
| const signal_params_t *signal_params) |
| { |
| display_data_t *display_data = signal_params->display_data; |
| /* highlight the nodes from this row */ |
| (display_data->set_menu)(tree_view, *signal_params->button_list, |
| path, ROW_LEFT_CLICKED); |
| /* display the full info */ |
| if (!enter_key_in_effect) |
| (display_data->set_menu)(tree_view, NULL, path, FULL_CLICKED); |
| enter_key_in_effect = false; |
| |
| return false; |
| } |
| |
| extern gboolean key_pressed(GtkTreeView *tree_view, |
| GdkEventKey *event, |
| const signal_params_t *signal_params) |
| { |
| control_key_in_effect = false; |
| enter_key_in_effect = false; |
| |
| if ((event->keyval == GDK_Control_L) || |
| (event->keyval == GDK_Control_R)) |
| control_key_in_effect = true; |
| else if (event->keyval == GDK_Return) { |
| each_t each; |
| GtkTreeSelection *selection = NULL; |
| |
| selection = gtk_tree_view_get_selection(tree_view); |
| memset(&each, 0, sizeof(each_t)); |
| each.tree_view = tree_view; |
| each.display_data = signal_params->display_data; |
| global_row_count = |
| gtk_tree_selection_count_selected_rows(selection); |
| popup_pos.x = 10; |
| popup_pos.x = 10; |
| popup_pos.cntr = 1; |
| popup_pos.slider = 0; |
| gtk_tree_selection_selected_foreach( |
| selection, _foreach_full_info, &each); |
| /*prevent row_activation from |
| * performing a redundant 'full info'*/ |
| enter_key_in_effect = true; |
| |
| } |
| |
| return false; |
| }/*key_pressed ^^^*/ |
| |
| |
| extern gboolean key_released(GtkTreeView *tree_view, |
| GdkEventKey *event, |
| const signal_params_t *signal_params) |
| { |
| |
| GtkTreePath *path = NULL; |
| GtkTreeViewColumn *column; |
| GtkTreeSelection *selection = NULL; |
| |
| if ((event->keyval != GDK_Up) && |
| (event->keyval != GDK_Down) && |
| (event->keyval != GDK_Return)) |
| return true; |
| |
| gtk_tree_view_get_cursor(GTK_TREE_VIEW(tree_view), &path, &column); |
| if (path) { |
| selection = gtk_tree_view_get_selection(tree_view); |
| gtk_tree_selection_select_path(selection, path); |
| gtk_tree_path_free(path); |
| } |
| return true; |
| |
| |
| }/*key_released ^^^*/ |
| |
| extern gboolean row_clicked(GtkTreeView *tree_view, GdkEventButton *event, |
| const signal_params_t *signal_params) |
| { |
| GtkTreePath *path = NULL; |
| GtkTreePath *last_path = NULL; |
| GtkTreeSelection *selection = NULL; |
| gboolean did_something = false; |
| gboolean selected_in_current_mix = false; |
| |
| if (!gtk_tree_view_get_path_at_pos(tree_view, |
| (gint) event->x, |
| (gint) event->y, |
| &path, NULL, NULL, NULL)) { |
| selection = gtk_tree_view_get_selection(tree_view); |
| |
| /* If there is a selection AND we are not under |
| * multi-selection processing via the ctrl key clear |
| * it up by doing a refresh. If there wasn't a |
| * selection before OR we are stacking selections via |
| * the ctrl key do nothing here. */ |
| if (gtk_tree_selection_count_selected_rows(selection)){ |
| if (!(event->state & GDK_CONTROL_MASK)) |
| gtk_tree_selection_unselect_all(selection); |
| refresh_main(NULL, NULL); |
| return true; |
| } |
| return false; |
| } |
| |
| /* make the selection (highlight) here */ |
| selection = gtk_tree_view_get_selection(tree_view); |
| global_row_count = |
| gtk_tree_selection_count_selected_rows(selection); |
| |
| /*flag this for rightclick to unselect on*/ |
| selected_in_current_mix = |
| gtk_tree_selection_path_is_selected(selection, path); |
| |
| if (event->button != 3) { |
| /*if Lshift is down then pull a range out*/ |
| if ((event->state & GDK_SHIFT_MASK)) { |
| if (last_event_x != 0) { |
| if (gtk_tree_view_get_path_at_pos( |
| tree_view, |
| (gint) last_event_x, |
| (gint) last_event_y, |
| &last_path, NULL, NULL, NULL)) { |
| if (last_path) { |
| gtk_tree_selection_select_range( |
| selection, last_path, |
| path); |
| gtk_tree_path_free(last_path); |
| } |
| } |
| } else if (path) { |
| /*ignore shift and pull single row anyway*/ |
| gtk_tree_selection_select_path(selection, |
| path); |
| } |
| } /*shift down^^*/ |
| } |
| last_event_x = event->x; /*save THIS x*/ |
| last_event_y = event->y; /*save THIS y*/ |
| |
| if (event->x <= 28) { |
| /* When you try to resize a column this event happens |
| for some reason. Resizing always happens in the |
| first 2 of x so if that happens just return and |
| continue. Also if we want to expand/collapse a |
| column, that happens in the first 28 (The default |
| expander size is 14, and as of writing this we |
| could expand 2 levels, so just skip |
| that also. If anyone in the future can figure out |
| a way to know for sure the expander was clicked |
| and not the actual column please fix this :). |
| */ |
| did_something = false; |
| } else if (event->button == 1) { |
| /* left click */ |
| if (!(event->state & GDK_CONTROL_MASK) |
| && !(event->state & GDK_SHIFT_MASK)) { |
| /* unselect current on naked left clicks..*/ |
| gtk_tree_selection_unselect_all(selection); |
| } |
| did_something = left_button_pressed( |
| tree_view, path, signal_params, event); |
| } else if (event->button == 3) { |
| /* right click */ |
| if (!selected_in_current_mix) { |
| if (!(event->state & GDK_CONTROL_MASK)){ |
| gtk_tree_selection_unselect_all(selection); |
| } else if (path) |
| gtk_tree_selection_select_path(selection, path); |
| } |
| global_row_count = |
| gtk_tree_selection_count_selected_rows(selection); |
| if (_DEBUG) |
| g_print("row_clicked:global_row_count2 : %d \n", |
| global_row_count); |
| /*prevent rc processing if under control/shift*/ |
| if (!(event->state & GDK_CONTROL_MASK) |
| && !(event->state & GDK_SHIFT_MASK)) |
| right_button_pressed(tree_view, path, event, |
| signal_params, ROW_CLICKED); |
| did_something = true; |
| } else if (!working_sview_config.admin_mode) |
| did_something = true; |
| gtk_tree_path_free(path); |
| |
| /* If control key held refresh main (which does the grid and |
| exit with false to reset the treeview. This has to happen |
| after left_button_pressed to get other things correct. */ |
| if (event->state & GDK_CONTROL_MASK) { |
| refresh_main(NULL, NULL); |
| return false; /*propagate event*/ |
| } |
| return did_something; |
| } |
| |
| extern popup_info_t *create_popup_info(int type, int dest_type, char *title) |
| { |
| GtkScrolledWindow *window = NULL, *grid_window = NULL; |
| GtkBin *bin = NULL; |
| GtkViewport *view = NULL; |
| GtkWidget *label = NULL; |
| GtkWidget *table = NULL; |
| GtkWidget *close_btn = NULL; |
| popup_info_t *popup_win = xmalloc(sizeof(popup_info_t)); |
| // int i=0; |
| |
| list_push(popup_list, popup_win); |
| |
| popup_win->spec_info = xmalloc(sizeof(specific_info_t)); |
| popup_win->spec_info->search_info = |
| xmalloc(sizeof(sview_search_info_t)); |
| popup_win->spec_info->search_info->search_type = 0; |
| popup_win->spec_info->search_info->gchar_data = NULL; |
| popup_win->spec_info->search_info->int_data = NO_VAL; |
| popup_win->spec_info->search_info->int_data2 = NO_VAL; |
| |
| popup_win->spec_info->type = type; |
| popup_win->spec_info->title = xstrdup(title); |
| popup_win->popup = gtk_dialog_new_with_buttons( |
| title, |
| GTK_WINDOW(main_window), |
| GTK_DIALOG_DESTROY_WITH_PARENT, |
| GTK_STOCK_REFRESH, |
| GTK_RESPONSE_OK, |
| NULL); |
| close_btn = gtk_dialog_add_button(GTK_DIALOG(popup_win->popup), |
| GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); |
| gtk_window_set_type_hint(GTK_WINDOW(popup_win->popup), |
| GDK_WINDOW_TYPE_HINT_NORMAL); |
| gtk_window_set_focus(GTK_WINDOW(popup_win->popup), close_btn); |
| gtk_dialog_add_button(GTK_DIALOG(popup_win->popup), |
| "Close All Popups", GTK_RESPONSE_CANCEL); |
| |
| |
| popup_win->show_grid = 1; |
| popup_win->toggled = 0; |
| popup_win->force_refresh = 0; |
| popup_win->type = dest_type; |
| popup_win->not_found = false; |
| /* |
| for(i=0;; i++) { |
| if (main_popup_positioner[i].width == -1) |
| break; |
| if (strstr(title,main_popup_positioner[i].name)) { |
| width = main_popup_positioner[i].width; |
| height = main_popup_positioner[i].height; |
| break; |
| } |
| } |
| */ |
| gtk_window_set_default_size(GTK_WINDOW(popup_win->popup), |
| working_sview_config.fi_popup_width, |
| working_sview_config.fi_popup_height); |
| gtk_window_set_transient_for(GTK_WINDOW(popup_win->popup), NULL); |
| |
| popup_win->event_box = gtk_event_box_new(); |
| label = gtk_label_new(popup_win->spec_info->title); |
| gtk_container_add(GTK_CONTAINER(popup_win->event_box), label); |
| |
| g_signal_connect(G_OBJECT(popup_win->event_box), |
| "button-press-event", |
| G_CALLBACK(redo_popup), |
| popup_win); |
| |
| gtk_event_box_set_above_child( |
| GTK_EVENT_BOX(popup_win->event_box), |
| false); |
| |
| gtk_box_pack_start(GTK_BOX(GTK_DIALOG(popup_win->popup)->vbox), |
| popup_win->event_box, false, false, 0); |
| |
| grid_window = create_scrolled_window(); |
| gtk_scrolled_window_set_policy(grid_window, |
| GTK_POLICY_NEVER, |
| GTK_POLICY_AUTOMATIC); |
| bin = GTK_BIN(&grid_window->container); |
| view = GTK_VIEWPORT(bin->child); |
| bin = GTK_BIN(&view->bin); |
| popup_win->grid_table = GTK_TABLE(bin->child); |
| popup_win->grid_button_list = NULL; |
| |
| table = gtk_table_new(1, 2, false); |
| |
| gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(grid_window), 0, 1, 0, 1, |
| GTK_SHRINK, GTK_EXPAND | GTK_FILL, |
| 0, 0); |
| |
| window = create_scrolled_window(); |
| bin = GTK_BIN(&window->container); |
| view = GTK_VIEWPORT(bin->child); |
| bin = GTK_BIN(&view->bin); |
| popup_win->table = GTK_TABLE(bin->child); |
| |
| gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(window), |
| 1, 2, 0, 1); |
| |
| gtk_box_pack_start(GTK_BOX(GTK_DIALOG(popup_win->popup)->vbox), |
| table, true, true, 0); |
| |
| g_signal_connect(G_OBJECT(popup_win->popup), "delete_event", |
| G_CALLBACK(delete_popup), |
| popup_win->spec_info->title); |
| g_signal_connect(G_OBJECT(popup_win->popup), "response", |
| G_CALLBACK(_handle_response), |
| popup_win); |
| g_signal_connect(G_OBJECT(popup_win->popup), "configure-event", |
| G_CALLBACK(_frame_callback), |
| popup_win); |
| gtk_window_move(GTK_WINDOW(popup_win->popup), |
| popup_pos.x, popup_pos.y); |
| gtk_widget_show_all(popup_win->popup); |
| |
| if (cluster_flags & CLUSTER_FLAG_FED) |
| gtk_widget_hide(GTK_WIDGET(grid_window)); |
| |
| return popup_win; |
| } |
| |
| extern void setup_popup_info(popup_info_t *popup_win, |
| display_data_t *display_data, |
| int cnt) |
| { |
| int i = 0; |
| |
| popup_win->display_data = xmalloc(sizeof(display_data_t)*(cnt+2)); |
| for(i=0; i<cnt+1; i++) { |
| memcpy(&popup_win->display_data[i], |
| &display_data[i], |
| sizeof(display_data_t)); |
| } |
| } |
| |
| extern void redo_popup(GtkWidget *widget, GdkEventButton *event, |
| popup_info_t *popup_win) |
| { |
| if (event && (event->button == 3)) { |
| GtkMenu *menu = GTK_MENU(gtk_menu_new()); |
| |
| (popup_win->display_data->set_menu)(popup_win, menu, |
| NULL, |
| POPUP_CLICKED); |
| |
| gtk_widget_show_all(GTK_WIDGET(menu)); |
| gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, |
| gdk_event_get_time((GdkEvent*)event)); |
| } |
| } |
| |
| extern void destroy_search_info(void *arg) |
| { |
| sview_search_info_t *search_info = (sview_search_info_t *)arg; |
| if (search_info) { |
| g_free(search_info->cluster_name); |
| if (search_info->gchar_data) |
| g_free(search_info->gchar_data); |
| search_info->gchar_data = NULL; |
| xfree(search_info); |
| search_info = NULL; |
| } |
| } |
| |
| extern void destroy_specific_info(void *arg) |
| { |
| specific_info_t *spec_info = (specific_info_t *)arg; |
| if (spec_info) { |
| xfree(spec_info->title); |
| |
| destroy_search_info(spec_info->search_info); |
| |
| if (spec_info->display_widget) { |
| gtk_widget_destroy(spec_info->display_widget); |
| spec_info->display_widget = NULL; |
| } |
| xfree(spec_info); |
| } |
| } |
| |
| extern void destroy_popup_info(void *arg) |
| { |
| popup_info_t *popup_win = (popup_info_t *)arg; |
| |
| if (popup_win) { |
| *popup_win->running = 0; |
| g_mutex_lock(sview_mutex); |
| /* these are all children of each other so must |
| be freed in this order */ |
| FREE_NULL_LIST(popup_win->grid_button_list); |
| if (popup_win->table) { |
| gtk_widget_destroy(GTK_WIDGET(popup_win->table)); |
| popup_win->table = NULL; |
| } |
| if (popup_win->grid_table) { |
| gtk_widget_destroy(GTK_WIDGET(popup_win->grid_table)); |
| popup_win->grid_table = NULL; |
| } |
| if (popup_win->event_box) { |
| gtk_widget_destroy(popup_win->event_box); |
| popup_win->event_box = NULL; |
| } |
| if (popup_win->popup) { |
| gtk_widget_destroy(popup_win->popup); |
| popup_win->popup = NULL; |
| } |
| |
| destroy_specific_info(popup_win->spec_info); |
| xfree(popup_win->display_data); |
| xfree(popup_win); |
| g_mutex_unlock(sview_mutex); |
| } |
| |
| } |
| |
| extern void destroy_signal_params(void *arg) |
| { |
| signal_params_t *signal_params = (signal_params_t *)arg; |
| |
| if (signal_params) { |
| xfree(signal_params); |
| } |
| } |
| |
| extern gboolean delete_popup(GtkWidget *widget, GtkWidget *event, char *title) |
| { |
| list_itr_t *itr = list_iterator_create(popup_list); |
| popup_info_t *popup_win = NULL; |
| |
| while ((popup_win = list_next(itr))) { |
| if (popup_win->spec_info) { |
| if (!xstrcmp(popup_win->spec_info->title, title)) { |
| //g_print("removing %s\n", title); |
| list_remove(itr); |
| destroy_popup_info(popup_win); |
| break; |
| } |
| } |
| } |
| list_iterator_destroy(itr); |
| |
| |
| return false; |
| } |
| |
| extern gboolean delete_popups(void) |
| { |
| list_itr_t *itr = list_iterator_create(popup_list); |
| popup_info_t *popup_win = NULL; |
| |
| while ((popup_win = list_next(itr))) { |
| list_remove(itr); |
| destroy_popup_info(popup_win); |
| } |
| list_iterator_destroy(itr); |
| |
| return false; |
| } |
| |
| extern void *popup_thr(popup_info_t *popup_win) |
| { |
| void (*specific_info) (popup_info_t *popup_win) = NULL; |
| int running = 1; |
| if (_DEBUG) |
| g_print("popup_thr:global_row_count = %d \n", |
| global_row_count); |
| switch(popup_win->type) { |
| case PART_PAGE: |
| specific_info = specific_info_part; |
| break; |
| case JOB_PAGE: |
| specific_info = specific_info_job; |
| break; |
| case NODE_PAGE: |
| specific_info = specific_info_node; |
| break; |
| case RESV_PAGE: |
| specific_info = specific_info_resv; |
| break; |
| case BB_PAGE: |
| specific_info = specific_info_bb; |
| break; |
| case SUBMIT_PAGE: |
| default: |
| g_print("thread got unknown type %d\n", popup_win->type); |
| return NULL; |
| } |
| /* this will switch to 0 when popup is closed. */ |
| popup_win->running = &running; |
| /* when popup is killed running will be set to 0 */ |
| while (running) { |
| gdk_threads_enter(); |
| (specific_info)(popup_win); |
| gdk_threads_leave(); |
| sleep(working_sview_config.refresh_delay); |
| } |
| popup_win->running = NULL; |
| return NULL; |
| } |
| |
| extern void set_for_update(GtkTreeModel *model, int updated) |
| { |
| GtkTreePath *path = gtk_tree_path_new_first(); |
| GtkTreeIter iter; |
| |
| /* mark all current rows as in need of an update. */ |
| if (path && gtk_tree_model_get_iter(model, &iter, path)) { |
| /* This process will make sure all iter's in the |
| * tree_store will be mark as needing to be updated. |
| * If it is still 0 after the update then it is old |
| * data and will be removed with remove_old() |
| */ |
| while (1) { |
| gtk_tree_store_set(GTK_TREE_STORE(model), &iter, |
| updated, 0, -1); |
| if (!gtk_tree_model_iter_next(model, &iter)) { |
| break; |
| } |
| } |
| } |
| |
| if (path) |
| gtk_tree_path_free(path); |
| } |
| |
| extern void remove_old(GtkTreeModel *model, int updated) |
| { |
| GtkTreePath *path = gtk_tree_path_new_first(); |
| GtkTreeIter iter; |
| int i; |
| |
| /* remove all old objects */ |
| if (gtk_tree_model_get_iter(model, &iter, path)) { |
| while (1) { |
| gtk_tree_model_get(model, &iter, updated, &i, -1); |
| if (!i) { |
| if (!gtk_tree_store_remove( |
| GTK_TREE_STORE(model), |
| &iter)) |
| break; |
| else |
| continue; |
| } |
| if (!gtk_tree_model_iter_next(model, &iter)) { |
| break; |
| } |
| } |
| } |
| gtk_tree_path_free(path); |
| } |
| |
| extern GtkWidget *create_pulldown_combo(display_data_t *display_data) |
| { |
| GtkListStore *store = NULL; |
| GtkWidget *combo = NULL; |
| GtkTreeIter iter; |
| GtkCellRenderer *renderer = NULL; |
| int i=0; |
| |
| store = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING); |
| while (display_data[i].id != -1) { |
| gtk_list_store_append(store, &iter); |
| gtk_list_store_set(store, &iter, 0, display_data[i].id, |
| 1, display_data[i].name, -1); |
| i++; |
| } |
| combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); |
| |
| g_object_unref(store); |
| renderer = gtk_cell_renderer_text_new(); |
| gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, true); |
| gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), renderer, |
| "text", 1); |
| |
| 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(void) |
| { |
| 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); |
| int response = 0; |
| char *user_name = NULL; |
| char time_str[256]; |
| time_t now = time(NULL); |
| |
| gtk_window_set_type_hint(GTK_WINDOW(popup), |
| GDK_WINDOW_TYPE_HINT_NORMAL); |
| |
| 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; |
| if (len == -1) { |
| xfree(reason_str); |
| reason_str = NULL; |
| goto end_it; |
| } |
| /* Append user, date and time */ |
| xstrcat(reason_str, " ["); |
| user_name = getlogin(); |
| if (user_name) |
| xstrcat(reason_str, user_name); |
| else |
| xstrfmtcat(reason_str, "%u", getuid()); |
| slurm_make_time_str(&now, time_str, sizeof(time_str)); |
| xstrfmtcat(reason_str, "@%s]", time_str); |
| } else |
| reason_str = xstrdup("cancelled"); |
| end_it: |
| gtk_widget_destroy(popup); |
| |
| return reason_str; |
| } |
| |
| extern void display_admin_edit(GtkTable *table, void *type_msg, int *row, |
| GtkTreeModel *model, GtkTreeIter *iter, |
| display_data_t *display_data, |
| GCallback changed_callback, |
| GCallback focus_callback, |
| void (*set_active)( |
| GtkComboBox *combo, |
| GtkTreeModel *model, GtkTreeIter *iter, |
| int type)) |
| { |
| GtkWidget *label = NULL; |
| GtkWidget *entry = NULL; |
| |
| if (display_data->extra == EDIT_MODEL) { |
| /* edittable items that can only be known |
| values */ |
| GtkCellRenderer *renderer = NULL; |
| GtkTreeModel *model2 = GTK_TREE_MODEL( |
| (display_data->create_model)(display_data->id)); |
| if (!model2) { |
| g_print("no model set up for %d(%s)\n", |
| display_data->id, |
| display_data->name); |
| return; |
| } |
| entry = gtk_combo_box_new_with_model(model2); |
| g_object_unref(model2); |
| |
| /* (callback)_set_active_combo_part(GTK_COMBO_BOX(entry), model, */ |
| /* iter, display_data->id); */ |
| (set_active)(GTK_COMBO_BOX(entry), model, |
| iter, display_data->id); |
| |
| g_signal_connect(entry, "changed", |
| changed_callback, |
| type_msg); |
| |
| renderer = gtk_cell_renderer_text_new(); |
| gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(entry), |
| renderer, true); |
| gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(entry), |
| renderer, "text", 0); |
| } else if (display_data->extra == EDIT_TEXTBOX) { |
| char *temp_char = NULL; |
| /* other edittable items that are unknown */ |
| entry = create_entry(); |
| if (model) { |
| gtk_tree_model_get(model, iter, |
| display_data->id, |
| &temp_char, -1); |
| } |
| gtk_entry_set_max_length(GTK_ENTRY(entry), |
| (DEFAULT_ENTRY_LENGTH + |
| display_data->id)); |
| |
| if (temp_char) { |
| gtk_entry_set_text(GTK_ENTRY(entry), |
| temp_char); |
| g_free(temp_char); |
| } |
| g_signal_connect(entry, "focus-out-event", |
| focus_callback, |
| type_msg); |
| |
| /* set global variable so we know something changed */ |
| g_signal_connect(entry, "changed", |
| G_CALLBACK(entry_changed), |
| NULL); |
| } else /* others can't be altered by the user */ |
| return; |
| label = gtk_label_new(display_data->name); |
| /* left justify */ |
| gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); |
| gtk_table_attach(table, label, 0, 1, *row, (*row)+1, |
| GTK_FILL | GTK_EXPAND, GTK_SHRINK, |
| 0, 0); |
| gtk_table_attach(table, entry, 1, 2, *row, (*row)+1, |
| GTK_FILL, GTK_SHRINK, |
| 0, 0); |
| (*row)++; |
| } |
| |
| extern void display_edit_note(char *edit_note) |
| { |
| GError *error = NULL; |
| int msg_id = 0; |
| |
| if (!edit_note) |
| return; |
| |
| gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), STATUS_ADMIN_EDIT); |
| msg_id = gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), |
| STATUS_ADMIN_EDIT, |
| edit_note); |
| if (!sview_thread_new(_editing_thr, GINT_TO_POINTER(msg_id), &error)) |
| g_printerr("Failed to create edit thread: %s\n", |
| error->message); |
| |
| return; |
| } |
| |
| extern void add_display_treestore_line(int update, |
| GtkTreeStore *treestore, |
| GtkTreeIter *iter, |
| const char *name, |
| const char *value) |
| { |
| if (!name) { |
| /* g_print("error, name = %s and value = %s\n", */ |
| /* name, value); */ |
| return; |
| } |
| if (update) { |
| char *display_name = NULL; |
| GtkTreePath *path = gtk_tree_path_new_first(); |
| gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore), iter, path); |
| |
| while (1) { |
| /* search for the jobid and check to see if |
| it is in the list */ |
| gtk_tree_model_get(GTK_TREE_MODEL(treestore), iter, |
| DISPLAY_NAME, |
| &display_name, -1); |
| if (!xstrcmp(display_name, name)) { |
| /* update with new info */ |
| g_free(display_name); |
| goto found; |
| } |
| g_free(display_name); |
| |
| if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(treestore), |
| iter)) { |
| return; |
| } |
| } |
| |
| } else { |
| gtk_tree_store_append(treestore, iter, NULL); |
| } |
| found: |
| gtk_tree_store_set(treestore, iter, |
| DISPLAY_NAME, name, |
| DISPLAY_VALUE, value, |
| -1); |
| |
| return; |
| } |
| |
| extern void add_display_treestore_line_with_font( |
| int update, |
| GtkTreeStore *treestore, |
| GtkTreeIter *iter, |
| const char *name, char *value, |
| char *font) |
| { |
| if (!name) { |
| /* g_print("error, name = %s and value = %s\n", */ |
| /* name, value); */ |
| return; |
| } |
| if (update) { |
| char *display_name = NULL; |
| GtkTreePath *path = gtk_tree_path_new_first(); |
| gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore), iter, path); |
| |
| while (1) { |
| /* search for the jobid and check to see if |
| it is in the list */ |
| gtk_tree_model_get(GTK_TREE_MODEL(treestore), iter, |
| DISPLAY_NAME, |
| &display_name, -1); |
| if (!xstrcmp(display_name, name)) { |
| /* update with new info */ |
| g_free(display_name); |
| goto found; |
| } |
| g_free(display_name); |
| |
| if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(treestore), |
| iter)) { |
| return; |
| } |
| } |
| |
| } else { |
| gtk_tree_store_append(treestore, iter, NULL); |
| } |
| found: |
| gtk_tree_store_set(treestore, iter, |
| DISPLAY_NAME, name, |
| DISPLAY_VALUE, value, |
| DISPLAY_FONT, font, |
| -1); |
| |
| return; |
| } |
| |
| extern void sview_widget_modify_bg(GtkWidget *widget, GtkStateType state, |
| const GdkColor color) |
| { |
| gtk_widget_modify_bg(widget, state, &color); |
| } |
| |
| extern void sview_radio_action_set_current_value(GtkRadioAction *action, |
| gint current_value) |
| { |
| #ifdef GTK2_USE_RADIO_SET |
| gtk_radio_action_set_current_value(action, current_value); |
| #else |
| GSList *slist, *group; |
| int i=0; |
| /* gtk_radio_action_set_current_value wasn't added to |
| GTK until 2.10, it turns out this is what is required to |
| set the correct value. |
| */ |
| g_return_if_fail(GTK_IS_RADIO_ACTION(action)); |
| |
| if ((group = gtk_radio_action_get_group(action))) { |
| /* for some reason groups are set backwards like a |
| stack, g_slist_reverse will fix this but takes twice |
| as long so just figure out the length, they add 1 |
| to it sense 0 isn't a number and then subtract the |
| value to get the augmented in the stack. |
| */ |
| current_value = g_slist_length(group) - 1 - current_value; |
| if (current_value < 0) { |
| g_warning("Radio group does not contain an action " |
| "with value '%d'\n", current_value); |
| return; |
| } |
| |
| for (slist = group; slist; slist = slist->next) { |
| if (i == current_value) { |
| gtk_toggle_action_set_active( |
| GTK_TOGGLE_ACTION(slist->data), true); |
| return; |
| } |
| i++; |
| } |
| } |
| #endif |
| } |
| |
| extern char *page_to_str(int page) |
| { |
| switch (page) { |
| case JOB_PAGE: |
| return "Job"; |
| case PART_PAGE: |
| return "Partition"; |
| case RESV_PAGE: |
| return "Reservation"; |
| case BB_PAGE: |
| return "BurstBuffer"; |
| case NODE_PAGE: |
| return "Node"; |
| default: |
| return NULL; |
| } |
| } |
| |
| extern char *tab_pos_to_str(int pos) |
| { |
| switch (pos) { |
| case GTK_POS_TOP: |
| return "Top"; |
| case GTK_POS_BOTTOM: |
| return "Bottom"; |
| case GTK_POS_LEFT: |
| return "Left"; |
| case GTK_POS_RIGHT: |
| return "Right"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| extern char *visible_to_str(sview_config_t *sview_config) |
| { |
| char *ret = NULL; |
| int i; |
| |
| for (i = 0; i < PAGE_CNT; i++) { |
| if (sview_config->page_visible[i] && (i != TAB_PAGE)) { |
| if (ret) |
| xstrcat(ret, ","); |
| xstrcat(ret, page_to_str(i)); |
| } |
| } |
| |
| return ret; |
| } |
| |
| extern gboolean entry_changed(GtkWidget *widget, void *msg) |
| { |
| global_entry_changed = 1; |
| return false; |
| } |
| |
| extern void select_admin_common(GtkTreeModel *model, GtkTreeIter *iter, |
| display_data_t *display_data, |
| GtkTreeView *treeview, |
| uint32_t node_col, |
| void (*process_each)(GtkTreeModel *model, |
| GtkTreePath *path, |
| GtkTreeIter *iter, |
| gpointer userdata)) |
| { |
| GtkTreePath *path; |
| GtkTreeRowReference *ref; |
| GtkTreeSelection *selection; |
| GList *list = NULL, *selected_rows = NULL; |
| |
| if (!treeview) |
| return; |
| |
| if (display_data->extra & EXTRA_NODES) { |
| select_admin_nodes(model, iter, display_data, |
| node_col, treeview); |
| return; |
| } |
| |
| global_multi_error = false; |
| |
| selection = gtk_tree_view_get_selection(treeview); |
| selected_rows = gtk_tree_selection_get_selected_rows(selection, &model); |
| |
| for (list = selected_rows; list; list = g_list_next(list)) { |
| ref = gtk_tree_row_reference_new(model, list->data); |
| path = gtk_tree_row_reference_get_path(ref); |
| (*(process_each))(model, path, iter, display_data->name); |
| gtk_tree_path_free(path); |
| gtk_tree_row_reference_free(ref); |
| } |
| |
| g_list_foreach(selected_rows, (GFunc)gtk_tree_path_free, NULL); |
| g_list_free(selected_rows); |
| |
| return; |
| } |
| |
| extern void set_column_width_fixed(GtkTreeView *tree_view, |
| int sortid, |
| int width) |
| { |
| GList* col_list; |
| GList *col; |
| |
| xassert(tree_view); |
| |
| if (!(col_list = gtk_tree_view_get_columns(tree_view))) |
| return; |
| |
| for (col = col_list; col; col = g_list_next(col)) { |
| if (gtk_tree_view_column_get_sort_column_id(col->data) == |
| sortid) { |
| gtk_tree_view_column_set_sizing( |
| col->data, GTK_TREE_VIEW_COLUMN_FIXED); |
| gtk_tree_view_column_set_fixed_width(col->data, width); |
| } |
| } |
| g_list_free(col_list); |
| } |