| /* |
| * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> |
| * |
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License (not later!) |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, see <http://www.gnu.org/licenses> |
| * |
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <gtk/gtk.h> |
| #include <glib-object.h> |
| |
| #include "trace-cmd.h" |
| #include "trace-local.h" |
| #include "trace-view.h" |
| #include "trace-gui.h" |
| #include "trace-compat.h" |
| #include "cpu.h" |
| #include "event-utils.h" |
| |
| enum { |
| COL_INDEX, |
| COL_CPU, |
| COL_TS, |
| COL_COMM, |
| COL_PID, |
| COL_LAT, |
| COL_EVENT, |
| COL_INFO, |
| NUM_COLS |
| }; |
| |
| static char* col_labels[] = { |
| "#", |
| "CPU", |
| "Time Stamp", |
| "Task", |
| "PID", |
| "Latency", |
| "Event", |
| "Info", |
| NULL |
| }; |
| static int col_chars[] = { |
| 0, /* INDEX */ |
| 0, /* CPU */ |
| 0, /* TS */ |
| 0, /* COMM */ |
| 0, /* PID */ |
| 0, /* LAT */ |
| 0, /* EVENT */ |
| 0, /* INFO */ |
| 0 |
| }; |
| |
| static GtkTreeModel * |
| create_trace_view_model(struct tracecmd_input *handle) |
| { |
| TraceViewStore *store; |
| |
| store = trace_view_store_new(handle); |
| |
| return GTK_TREE_MODEL(store); |
| } |
| |
| static void |
| spin_changed(gpointer data, GtkWidget *spin) |
| { |
| GtkTreeView *tree = data; |
| GtkTreeModel *model; |
| gint val, page; |
| |
| val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); |
| |
| model = gtk_tree_view_get_model(tree); |
| /* This can be called when we NULL out the model */ |
| if (!model) |
| return; |
| page = trace_view_store_get_page(TRACE_VIEW_STORE(model)); |
| if (page == val) |
| return; |
| |
| g_object_ref(model); |
| gtk_tree_view_set_model(tree, NULL); |
| |
| trace_view_store_set_page(TRACE_VIEW_STORE(model), val); |
| |
| gtk_tree_view_set_model(tree, model); |
| g_object_unref(model); |
| } |
| |
| void trace_view_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer, |
| GtkTreeModel *model, GtkTreeIter *iter, |
| gpointer data) |
| { |
| long col_num = (long)data; |
| int str_len, label_len; |
| gchar *text, *str; |
| int new_w, x_pad; |
| GValue val = {0}; |
| GtkWidget *view; |
| |
| PangoFontDescription *pfd; |
| PangoLayout *playout; |
| |
| /* Put the text in the renderer. */ |
| gtk_tree_model_get_value(model, iter, col_num, &val); |
| g_object_set_property(G_OBJECT(renderer), "text", &val); |
| |
| g_object_get(G_OBJECT(renderer), |
| "text", &text, |
| "font-desc", &pfd, |
| NULL); |
| |
| if (!text) |
| goto out; |
| |
| /* Make sure there is enough room to render the column label. */ |
| str = text; |
| str_len = strlen(str); |
| label_len = strlen(col_labels[col_num]); |
| if (label_len > str_len) { |
| str = col_labels[col_num]; |
| str_len = label_len; |
| } |
| |
| /* Don't bother with pango unless we have more chars than the max. */ |
| if (str_len > col_chars[col_num]) { |
| col_chars[col_num] = str_len; |
| |
| view = GTK_WIDGET(gtk_tree_view_column_get_tree_view(column)); |
| playout = gtk_widget_create_pango_layout(GTK_WIDGET(view), str); |
| pango_layout_set_font_description(playout, pfd); |
| pango_layout_get_pixel_size(playout, &new_w, NULL); |
| gtk_cell_renderer_get_padding(renderer, &x_pad, NULL); |
| /* +10 to avoid another adjustment for one char */ |
| new_w += 2*x_pad + 10; |
| g_object_unref(playout); |
| |
| if (new_w > gtk_tree_view_column_get_width(column)) |
| gtk_tree_view_column_set_fixed_width(column, new_w); |
| } |
| |
| g_free(text); |
| out: |
| pango_font_description_free(pfd); |
| g_value_unset(&val); |
| } |
| |
| void |
| trace_view_load(GtkWidget *view, struct tracecmd_input *handle, |
| GtkWidget *spin) |
| { |
| GtkCellRenderer *renderer; |
| GtkCellRenderer *fix_renderer; |
| GtkTreeModel *model; |
| long c; |
| |
| /* |
| * The handle should have loaded the file by now. |
| * Try to turn off function trace indent and turn on show parent |
| * if possible. |
| */ |
| trace_util_add_option("ftrace:parent", "1"); |
| trace_util_add_option("ftrace:indent", "0"); |
| |
| /* Also, show the function name in the tail for function graph */ |
| trace_util_add_option("fgraph:tailprint", "1"); |
| |
| /* --- CPU column --- */ |
| |
| renderer = gtk_cell_renderer_text_new(); |
| fix_renderer = gtk_cell_renderer_text_new(); |
| |
| g_object_set(fix_renderer, |
| "family", "Monospace", |
| "family-set", TRUE, |
| NULL); |
| |
| /* |
| * Set fixed height mode now which will cause all the columns below to |
| * be created with their sizing property to be set to |
| * GTK_TREE_VIEW_COLUMN_FIXED. |
| */ |
| gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(view), TRUE); |
| |
| for (c = 0; c < NUM_COLS; c++) |
| { |
| gtk_tree_view_insert_column_with_data_func(GTK_TREE_VIEW(view), |
| -1, |
| col_labels[c], |
| (c == COL_LAT || c == COL_INFO) ? fix_renderer : renderer, |
| trace_view_data_func, |
| (gpointer)c, |
| NULL); |
| } |
| |
| g_signal_connect_swapped (G_OBJECT (spin), "value-changed", |
| G_CALLBACK (spin_changed), |
| (gpointer) view); |
| |
| |
| if (handle) { |
| model = create_trace_view_model(handle); |
| trace_view_store_set_spin_button(TRACE_VIEW_STORE(model), spin); |
| gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); |
| g_object_unref(model); /* destroy model automatically with view */ |
| } |
| } |
| |
| void trace_view_reload(GtkWidget *view, struct tracecmd_input *handle, |
| GtkWidget *spin) |
| { |
| GtkTreeModel *model; |
| |
| if (!handle) |
| return; |
| |
| model = create_trace_view_model(handle); |
| if (!model) |
| return; |
| trace_view_store_set_spin_button(TRACE_VIEW_STORE(model), spin); |
| gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); |
| |
| } |
| |
| /** |
| * trace_view_get_selected_row - return the selected row |
| * @treeview: The tree view |
| * |
| * Returns the selected row number (or -1 if none is selected) |
| */ |
| gint trace_view_get_selected_row(GtkWidget *treeview) |
| { |
| GtkTreeView *tree = GTK_TREE_VIEW(treeview); |
| GtkTreeSelection *selection; |
| GtkTreeModel *model; |
| GtkTreePath *path; |
| gchar *spath; |
| GList *glist; |
| gint row; |
| |
| model = gtk_tree_view_get_model(tree); |
| if (!model) |
| return -1; |
| |
| selection = gtk_tree_view_get_selection(tree); |
| glist = gtk_tree_selection_get_selected_rows(selection, &model); |
| if (!glist) |
| return -1; |
| |
| /* Only one row may be selected */ |
| path = glist->data; |
| spath = gtk_tree_path_to_string(path); |
| row = atoi(spath); |
| g_free(spath); |
| |
| gtk_tree_path_free(path); |
| g_list_free(glist); |
| |
| return row; |
| } |
| |
| void trace_view_make_selection_visible(GtkWidget *treeview) |
| { |
| GtkTreeView *tree = GTK_TREE_VIEW(treeview); |
| GtkTreePath *path; |
| gchar *spath; |
| GString *gstr; |
| gint row; |
| |
| row = trace_view_get_selected_row(treeview); |
| if (row < 0) |
| return; |
| |
| gstr = g_string_new(""); |
| g_string_printf(gstr, "%d", row); |
| spath = g_string_free(gstr, FALSE); |
| |
| path = gtk_tree_path_new_from_string(spath); |
| g_free(spath); |
| |
| gtk_tree_view_scroll_to_cell(tree, path, NULL, TRUE, 0.5, 0.0); |
| |
| gtk_tree_path_free(path); |
| } |
| |
| void trace_view_update_filters(GtkWidget *treeview, |
| struct filter_task *task_filter, |
| struct filter_task *hide_tasks) |
| { |
| GtkTreeView *tree = GTK_TREE_VIEW(treeview); |
| TraceViewRecord *vrec; |
| GtkTreeModel *model; |
| guint64 time; |
| gint row; |
| |
| model = gtk_tree_view_get_model(tree); |
| if (!model) |
| return; |
| |
| /* Keep track of the currently selected row */ |
| row = trace_view_get_selected_row(treeview); |
| if (row >= 0) { |
| vrec = trace_view_store_get_row(TRACE_VIEW_STORE(model), row); |
| time = vrec->timestamp; |
| } |
| |
| g_object_ref(model); |
| gtk_tree_view_set_model(tree, NULL); |
| |
| trace_view_store_assign_filters(TRACE_VIEW_STORE(model), task_filter, hide_tasks); |
| trace_view_store_update_filter(TRACE_VIEW_STORE(model)); |
| |
| gtk_tree_view_set_model(tree, model); |
| g_object_unref(model); |
| |
| /* Keep selection near previous selection */ |
| if (row >= 0) |
| trace_view_select(treeview, time); |
| } |
| |
| static void select_row_from_path(GtkTreeView *tree, GtkTreePath *path) |
| { |
| GtkTreeSelection *selection; |
| |
| selection = gtk_tree_view_get_selection(tree); |
| gtk_tree_view_set_cursor(tree, path, NULL, FALSE); |
| } |
| |
| void trace_view_select(GtkWidget *treeview, guint64 time) |
| { |
| GtkTreeView *tree = GTK_TREE_VIEW(treeview); |
| GtkTreeModel *model; |
| GtkTreePath *path; |
| gint select_page, page; |
| GtkWidget *spin; |
| gchar buf[100]; |
| gint row; |
| |
| model = gtk_tree_view_get_model(tree); |
| /* This can be called when we NULL out the model */ |
| if (!model) |
| return; |
| |
| if (!trace_view_store_visible_rows(TRACE_VIEW_STORE(model))) |
| return; |
| |
| page = trace_view_store_get_page(TRACE_VIEW_STORE(model)); |
| select_page = trace_view_store_get_timestamp_page(TRACE_VIEW_STORE(model), |
| time); |
| |
| /* Make sure the page contains the selected event */ |
| if (page != select_page) { |
| spin = trace_view_store_get_spin(TRACE_VIEW_STORE(model)); |
| /* If a spin button exists, it should update when changed */ |
| if (spin) |
| gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), select_page); |
| else { |
| g_object_ref(model); |
| gtk_tree_view_set_model(tree, NULL); |
| |
| trace_view_store_set_page(TRACE_VIEW_STORE(model), select_page); |
| |
| gtk_tree_view_set_model(tree, model); |
| g_object_unref(model); |
| } |
| } |
| |
| /* Select the event */ |
| row = trace_view_store_get_timestamp_visible_row(TRACE_VIEW_STORE(model), time); |
| snprintf(buf, 100, "%d", row); |
| path = gtk_tree_path_new_from_string(buf); |
| select_row_from_path(tree, path); |
| gtk_tree_path_free(path); |
| } |
| |
| static void update_rows(GtkTreeView *trace_tree, TraceViewStore *store) |
| { |
| TraceViewRecord *vrec; |
| guint64 time; |
| gint row; |
| |
| /* Keep track of the currently selected row */ |
| row = trace_view_get_selected_row(GTK_WIDGET(trace_tree)); |
| if (row >= 0) { |
| vrec = trace_view_store_get_row(store, row); |
| time = vrec->timestamp; |
| } |
| |
| /* Force an update */ |
| g_object_ref(store); |
| gtk_tree_view_set_model(trace_tree, NULL); |
| trace_view_store_update_filter(store); |
| gtk_tree_view_set_model(trace_tree, GTK_TREE_MODEL(store)); |
| g_object_unref(store); |
| |
| if (row >= 0) |
| trace_view_select(GTK_WIDGET(trace_tree), time); |
| } |
| |
| void trace_view_event_filter_callback(gboolean accept, |
| gboolean all_events, |
| gchar **systems, |
| gint *events, |
| gpointer data) |
| { |
| struct event_filter *event_filter; |
| GtkTreeView *trace_tree = data; |
| GtkTreeModel *model; |
| TraceViewStore *store; |
| |
| if (!accept) |
| return; |
| |
| model = gtk_tree_view_get_model(trace_tree); |
| if (!model) |
| return; |
| |
| store = TRACE_VIEW_STORE(model); |
| |
| if (all_events) { |
| if (trace_view_store_get_all_events_enabled(store)) |
| return; |
| |
| trace_view_store_set_all_events_enabled(store); |
| } else { |
| trace_view_store_clear_all_events_enabled(store); |
| |
| event_filter = trace_view_store_get_event_filter(store); |
| |
| trace_filter_convert_char_to_filter(event_filter, systems, events); |
| } |
| |
| update_rows(trace_tree, store); |
| } |
| |
| void trace_view_adv_filter_callback(gboolean accept, |
| const gchar *text, |
| gint *event_ids, |
| gpointer data) |
| { |
| struct event_filter *event_filter; |
| GtkTreeView *trace_tree = data; |
| GtkTreeModel *model; |
| TraceViewStore *store; |
| char *error_str; |
| int ret; |
| int i; |
| |
| if (!accept) |
| return; |
| |
| model = gtk_tree_view_get_model(trace_tree); |
| if (!model) |
| return; |
| |
| if (!has_text(text) && !event_ids) |
| return; |
| |
| store = TRACE_VIEW_STORE(model); |
| |
| event_filter = trace_view_store_get_event_filter(store); |
| |
| if (event_ids) { |
| for (i = 0; event_ids[i] >= 0; i++) |
| pevent_filter_remove_event(event_filter, event_ids[i]); |
| } |
| |
| if (has_text(text)) { |
| |
| trace_view_store_clear_all_events_enabled(store); |
| |
| ret = pevent_filter_add_filter_str(event_filter, text, &error_str); |
| if (ret < 0) { |
| warning("filter failed due to: %s", error_str); |
| free(error_str); |
| return; |
| } |
| } |
| |
| update_rows(trace_tree, store); |
| } |
| |
| void trace_view_copy_filter(GtkWidget *treeview, |
| gboolean all_events, |
| struct event_filter *src_event_filter) |
| { |
| GtkTreeView *trace_tree; |
| struct event_filter *event_filter; |
| GtkTreeModel *model; |
| TraceViewStore *store; |
| |
| trace_tree = GTK_TREE_VIEW(treeview); |
| |
| model = gtk_tree_view_get_model(trace_tree); |
| if (!model) |
| return; |
| |
| store = TRACE_VIEW_STORE(model); |
| |
| if (all_events) { |
| if (trace_view_store_get_all_events_enabled(store)) |
| return; |
| |
| trace_view_store_set_all_events_enabled(store); |
| } else { |
| trace_view_store_clear_all_events_enabled(store); |
| |
| event_filter = trace_view_store_get_event_filter(store); |
| |
| pevent_filter_copy(event_filter, src_event_filter); |
| } |
| |
| update_rows(trace_tree, store); |
| } |
| |
| void trace_view_cpu_filter_callback(gboolean accept, |
| gboolean all_cpus, |
| guint64 *selected_cpu_mask, |
| gpointer data) |
| { |
| GtkTreeView *trace_tree = data; |
| TraceViewRecord *rec; |
| GtkTreeModel *model; |
| TraceViewStore *store; |
| guint64 time = 0; |
| gint selected_row; |
| gint cpus; |
| gint cpu; |
| |
| if (!accept) |
| return; |
| |
| model = gtk_tree_view_get_model(trace_tree); |
| if (!model) |
| return; |
| |
| store = TRACE_VIEW_STORE(model); |
| |
| selected_row = trace_view_get_selected_row(GTK_WIDGET(trace_tree)); |
| if (selected_row < 0) |
| selected_row = 0; |
| |
| g_object_ref(store); |
| gtk_tree_view_set_model(trace_tree, NULL); |
| |
| /* |
| * If the selected row is not part of one of the CPUs |
| * that are kept, then find one that is. Do nothing if |
| * the first row is selected. |
| */ |
| if (selected_row) { |
| /* Save this timestamp */ |
| rec = trace_view_store_get_visible_row(TRACE_VIEW_STORE(model), selected_row); |
| time = rec->timestamp; |
| } |
| |
| if (all_cpus) { |
| trace_view_store_set_all_cpus(store); |
| goto set_model; |
| } |
| |
| cpus = trace_view_store_get_cpus(store); |
| |
| for (cpu = 0; cpu < cpus; cpu++) { |
| if (cpu_isset(selected_cpu_mask, cpu)) |
| trace_view_store_set_cpu(store, cpu); |
| else |
| trace_view_store_clear_cpu(store, cpu); |
| } |
| |
| set_model: |
| gtk_tree_view_set_model(trace_tree, GTK_TREE_MODEL(store)); |
| g_object_unref(store); |
| |
| if (!time) |
| return; |
| /* |
| * Try to select the row that was near the selection |
| * before the change. |
| */ |
| trace_view_select(GTK_WIDGET(trace_tree), time); |
| } |
| |
| static GtkTreeModel *create_col_model(GtkTreeView *treeview) |
| { |
| GtkListStore *store; |
| GtkTreeViewColumn *col; |
| GtkTreeIter iter; |
| const gchar *title; |
| int i; |
| |
| store = gtk_list_store_new(1, G_TYPE_STRING); |
| |
| i = 0; |
| col = gtk_tree_view_get_column(treeview, i++); |
| while (col) { |
| title = gtk_tree_view_column_get_title(col); |
| if (!title) |
| break; |
| |
| gtk_list_store_append(store, &iter); |
| gtk_list_store_set(store, &iter, |
| 0, title, |
| -1); |
| |
| col = gtk_tree_view_get_column(treeview, i++); |
| } |
| |
| return GTK_TREE_MODEL(store); |
| } |
| |
| struct search_info { |
| GtkTreeView *treeview; |
| GtkWidget *entry; |
| GtkWidget *selection; |
| GtkWidget *column; |
| }; |
| |
| #define SELECTION_NAMES \ |
| C( contains, CONTAINS ), \ |
| C( full match, FULL_MATCH ), \ |
| C( does not have, NOT_IN ) |
| |
| #undef C |
| #define C(a, b) #a |
| |
| static gchar *select_names[] = { SELECTION_NAMES, NULL }; |
| |
| #undef C |
| #define C(a, b) SEL_##b |
| |
| enum select_options { SELECTION_NAMES }; |
| |
| static gboolean test_int(gint val, gint search_val, enum select_options sel) |
| { |
| gint tens; |
| gboolean match = TRUE; |
| |
| switch (sel) { |
| case SEL_NOT_IN: |
| match = FALSE; |
| case SEL_CONTAINS: |
| for (tens = 1; search_val / tens; tens *= 10) |
| ; |
| |
| while (val) { |
| if (val - search_val == (val / tens) * tens) |
| return match; |
| val /= 10; |
| } |
| return !match; |
| |
| case SEL_FULL_MATCH: |
| return search_val == val; |
| } |
| return FALSE; |
| } |
| |
| static gboolean test_text(const gchar *text, const gchar *search_text, enum select_options sel) |
| { |
| gboolean match = TRUE; |
| |
| switch (sel) { |
| case SEL_NOT_IN: |
| match = FALSE; |
| case SEL_CONTAINS: |
| |
| text = strcasestr(text, search_text); |
| if (text) |
| return match; |
| return !match; |
| |
| case SEL_FULL_MATCH: |
| return strcmp(text, search_text) == 0; |
| } |
| return FALSE; |
| } |
| |
| static gboolean test_row(GtkTreeModel *model, GtkTreeIter *iter, gint sel, |
| gint col_num, gint search_val, const gchar *search_text) |
| { |
| gchar *text = NULL; |
| gboolean found = FALSE; |
| gint val; |
| |
| switch (col_num) { |
| case TRACE_VIEW_STORE_COL_INDEX: |
| case TRACE_VIEW_STORE_COL_CPU: |
| case TRACE_VIEW_STORE_COL_PID: |
| /* integers */ |
| |
| gtk_tree_model_get(model, iter, |
| col_num, &val, |
| -1); |
| if (test_int(val, search_val, sel)) |
| found = TRUE; |
| break; |
| |
| case TRACE_VIEW_STORE_COL_TS: |
| case TRACE_VIEW_STORE_COL_COMM: |
| case TRACE_VIEW_STORE_COL_LAT: |
| case TRACE_VIEW_STORE_COL_EVENT: |
| case TRACE_VIEW_STORE_COL_INFO: |
| /* strings */ |
| |
| gtk_tree_model_get(model, iter, |
| col_num, &text, |
| -1); |
| |
| if (test_text(text, search_text, sel)) |
| found = TRUE; |
| break; |
| } |
| |
| return found; |
| } |
| |
| static void search_next_pages(GtkTreeView *tree, |
| TraceViewStore *store, gint sel, gint col_num, |
| gint search_val, const char *search_text) |
| { |
| GtkTreeModel *model; |
| TraceViewRecord *rec; |
| GtkTreeIter iter; |
| gint row; |
| gint total_rows; |
| gboolean found = FALSE; |
| |
| model = (GtkTreeModel *)store; |
| |
| row = store->start_row + store->num_rows; |
| total_rows = trace_view_store_get_num_actual_rows(store); |
| |
| trace_set_cursor(GDK_WATCH); |
| trace_freeze_all(); |
| |
| for (; row < total_rows; row++) { |
| |
| /* Needed to process the cursor change */ |
| if (!(row & ((1 << 5)-1))) |
| gtk_main_iteration_do(FALSE); |
| |
| rec = trace_view_store_get_actual_row(store, row); |
| iter.user_data = rec; |
| found = test_row(model, &iter, sel, col_num, search_val, search_text); |
| if (found) |
| break; |
| } |
| trace_unfreeze_all(); |
| trace_put_cursor(); |
| |
| if (!found) { |
| trace_dialog(NULL, TRACE_GUI_INFO, "Not found"); |
| return; |
| } |
| |
| trace_view_select(GTK_WIDGET(tree), rec->timestamp); |
| } |
| |
| static void search_tree(gpointer data) |
| { |
| struct search_info *info = data; |
| GtkTreePath *path; |
| GtkTreeViewColumn *col; |
| GtkTreeModel *model; |
| TraceViewStore *store; |
| GtkTreeIter iter; |
| GtkEntry *entry = GTK_ENTRY(info->entry); |
| GtkComboBox *col_combo = GTK_COMBO_BOX(info->column); |
| GtkComboBox *sel_combo = GTK_COMBO_BOX(info->selection); |
| const gchar *title; |
| const gchar *search_text; |
| gint col_num; |
| gint sel; |
| gint search_val; |
| gint start_row; |
| gboolean found = FALSE; |
| gint i = 0; |
| |
| col_num = gtk_combo_box_get_active(col_combo); |
| sel = gtk_combo_box_get_active(sel_combo); |
| |
| if (col_num >= TRACE_VIEW_STORE_N_COLUMNS) |
| return; |
| |
| search_text = gtk_entry_get_text(entry); |
| if (!search_text || !strlen(search_text)) |
| return; |
| |
| col = gtk_tree_view_get_column(info->treeview, col_num); |
| if (!col) |
| return; |
| |
| title = gtk_tree_view_column_get_title(col); |
| if (!title) |
| return; |
| |
| model = gtk_tree_view_get_model(info->treeview); |
| if (!model) |
| return; |
| |
| store = TRACE_VIEW_STORE(model); |
| |
| if (!trace_view_store_visible_rows(store)) |
| return; |
| |
| start_row = trace_view_get_selected_row(GTK_WIDGET(info->treeview)); |
| if (start_row < 0) |
| start_row = 0; |
| |
| if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, start_row)) |
| return; |
| |
| trace_set_cursor(GDK_WATCH); |
| trace_freeze_all(); |
| |
| search_val = atoi(search_text); |
| while (gtk_tree_model_iter_next(model, &iter)) { |
| /* Needed to process the cursor change */ |
| if (!(i++ & ((1 << 5)-1))) |
| gtk_main_iteration_do(FALSE); |
| |
| found = test_row(model, &iter, sel, col_num, search_val, search_text); |
| if (found) |
| break; |
| } |
| trace_unfreeze_all(); |
| trace_put_cursor(); |
| |
| if (!found) { |
| GtkResponseType ret; |
| gint pages = trace_view_store_get_pages(store); |
| gint page = trace_view_store_get_page(store); |
| |
| if (page < pages) { |
| ret = trace_dialog(NULL, TRACE_GUI_ASK, |
| "Not found on this page\n" |
| "Search next pages?"); |
| if (ret == GTK_RESPONSE_YES) |
| search_next_pages(info->treeview, store, sel, |
| col_num, search_val, search_text); |
| return; |
| } |
| trace_dialog(NULL, TRACE_GUI_INFO, "Not found"); |
| return; |
| } |
| |
| path = gtk_tree_model_get_path(model, &iter); |
| select_row_from_path(info->treeview, path); |
| gtk_tree_path_free(path); |
| } |
| |
| void trace_view_search_setup(GtkBox *box, GtkTreeView *treeview) |
| { |
| GtkCellRenderer *renderer; |
| GtkListStore *store; |
| GtkTreeModel *model; |
| GtkTreeIter iter; |
| GtkWidget *label; |
| GtkWidget *col_combo; |
| GtkWidget *sel_combo; |
| GtkWidget *entry; |
| gchar **selects = select_names; |
| int i; |
| struct search_info *info; |
| |
| renderer = gtk_cell_renderer_text_new(); |
| |
| info = g_new0(typeof(*info), 1); |
| info->treeview = treeview; |
| |
| label = gtk_label_new("Column: "); |
| gtk_box_pack_start(box, label, FALSE, FALSE, 0); |
| gtk_widget_show(label); |
| |
| /* --- Set up the column selection combo box --- */ |
| |
| model = create_col_model(treeview); |
| |
| col_combo = gtk_combo_box_new_with_model(model); |
| gtk_box_pack_start(box, col_combo, FALSE, FALSE, 0); |
| gtk_widget_show(col_combo); |
| |
| /* Free model with combobox */ |
| g_object_unref(model); |
| |
| gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(col_combo), |
| renderer, |
| TRUE); |
| gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(col_combo), |
| renderer, |
| "text", 0, |
| NULL); |
| |
| gtk_combo_box_set_active(GTK_COMBO_BOX(col_combo), 0); |
| |
| info->column = col_combo; |
| |
| /* --- Set up the column selection combo box --- */ |
| |
| store = gtk_list_store_new(1, G_TYPE_STRING); |
| model = GTK_TREE_MODEL(store); |
| |
| sel_combo = gtk_combo_box_new_with_model(model); |
| gtk_box_pack_start(box, sel_combo, FALSE, FALSE, 0); |
| gtk_widget_show(sel_combo); |
| |
| info->selection = sel_combo; |
| |
| gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(sel_combo), |
| renderer, |
| TRUE); |
| gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(sel_combo), |
| renderer, |
| "text", 0, |
| NULL); |
| |
| for (i = 0; selects[i]; i++ ) { |
| gtk_list_store_append(store, &iter); |
| gtk_list_store_set(store, &iter, |
| 0, selects[i], |
| -1); |
| } |
| |
| gtk_combo_box_set_active(GTK_COMBO_BOX(sel_combo), 0); |
| |
| /* --- The text entry --- */ |
| |
| entry = gtk_entry_new(); |
| gtk_box_pack_start(box, entry, FALSE, FALSE, 0); |
| gtk_widget_show(entry); |
| |
| info->entry = entry; |
| |
| g_signal_connect_swapped (entry, "activate", |
| G_CALLBACK (search_tree), |
| (gpointer) info); |
| } |
| |
| int trace_view_save_filters(struct tracecmd_xml_handle *handle, |
| GtkTreeView *trace_tree) |
| { |
| struct event_filter *event_filter; |
| GtkTreeModel *model; |
| TraceViewStore *store; |
| gboolean all_events; |
| |
| model = gtk_tree_view_get_model(trace_tree); |
| if (!model) |
| return -1; |
| |
| store = TRACE_VIEW_STORE(model); |
| |
| tracecmd_xml_start_system(handle, "TraceView"); |
| |
| all_events = trace_view_store_get_all_events_enabled(store); |
| event_filter = trace_view_store_get_event_filter(store); |
| |
| tracecmd_xml_start_sub_system(handle, "EventFilter"); |
| |
| if (all_events || !event_filter) |
| tracecmd_xml_write_element(handle, "FilterType", "all events"); |
| else { |
| tracecmd_xml_write_element(handle, "FilterType", "filter"); |
| trace_filter_save_events(handle, event_filter); |
| } |
| |
| tracecmd_xml_end_sub_system(handle); |
| |
| tracecmd_xml_end_system(handle); |
| |
| return 0; |
| } |
| |
| static int load_event_filter(TraceViewStore *store, |
| struct tracecmd_xml_handle *handle, |
| struct tracecmd_xml_system_node *node) |
| { |
| struct tracecmd_xml_system_node *child; |
| struct event_filter *event_filter; |
| const char *name; |
| const char *value; |
| |
| event_filter = trace_view_store_get_event_filter(store); |
| |
| child = tracecmd_xml_node_child(node); |
| name = tracecmd_xml_node_type(child); |
| if (strcmp(name, "FilterType") != 0) |
| return -1; |
| |
| value = tracecmd_xml_node_value(handle, child); |
| /* Do nothing with all events enabled */ |
| if (strcmp(value, "all events") == 0) |
| return 0; |
| |
| node = tracecmd_xml_node_next(child); |
| if (!node) |
| return -1; |
| |
| trace_view_store_clear_all_events_enabled(store); |
| |
| trace_filter_load_events(event_filter, handle, node); |
| |
| return 0; |
| } |
| |
| int trace_view_load_filters(struct tracecmd_xml_handle *handle, |
| GtkTreeView *trace_tree) |
| { |
| struct tracecmd_xml_system *system; |
| struct tracecmd_xml_system_node *syschild; |
| GtkTreeModel *model; |
| TraceViewStore *store; |
| const char *name; |
| |
| model = gtk_tree_view_get_model(trace_tree); |
| if (!model) |
| return -1; |
| |
| store = TRACE_VIEW_STORE(model); |
| |
| system = tracecmd_xml_find_system(handle, "TraceView"); |
| if (!system) |
| return -1; |
| |
| syschild = tracecmd_xml_system_node(system); |
| if (!syschild) |
| goto out_free_sys; |
| |
| do { |
| name = tracecmd_xml_node_type(syschild); |
| |
| if (strcmp(name, "EventFilter") == 0) |
| load_event_filter(store, handle, syschild); |
| |
| syschild = tracecmd_xml_node_next(syschild); |
| } while (syschild); |
| |
| tracecmd_xml_free_system(system); |
| |
| update_rows(trace_tree, store); |
| return 0; |
| |
| out_free_sys: |
| tracecmd_xml_free_system(system); |
| return -1; |
| } |