| /* |
| * 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 <gtk/gtk.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <sys/stat.h> |
| |
| #include "trace-compat.h" |
| #include "trace-cmd.h" |
| #include "trace-gui.h" |
| |
| #define DIALOG_WIDTH 500 |
| #define DIALOG_HEIGHT 550 |
| |
| static GtkWidget *statusbar; |
| static GtkWidget *statuspix; |
| static GString *statusstr; |
| |
| static GtkWidget *parent_window; |
| |
| static void (*alt_warning)(const char *fmt, va_list ap); |
| |
| void vpr_stat(const char *fmt, va_list ap) |
| { |
| GString *str; |
| |
| if (!statusstr) { |
| statusstr = g_string_new(""); |
| if (!statusstr) |
| die("Allocating status string"); |
| } |
| |
| str = g_string_new(""); |
| |
| g_string_vprintf(str, fmt, ap); |
| |
| g_string_append_printf(statusstr, "%s\n", str->str); |
| |
| if (statusbar) { |
| gtk_statusbar_push(GTK_STATUSBAR(statusbar), 1, str->str); |
| gtk_widget_show(statuspix); |
| } |
| |
| g_string_free(str, TRUE); |
| } |
| |
| void pr_stat(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| vpr_stat(fmt, ap); |
| va_end(ap); |
| } |
| |
| /** |
| * trace_dialog_register_window - register window for warning dialogs |
| * @window: parent window to use for other dialogs |
| * |
| * The warning messages do not have a way to pass the window to |
| * the function, since these functions are also used by the command |
| * line interface. This allows an application to give the warning |
| * messages a window to use. |
| */ |
| void trace_dialog_register_window(GtkWidget *window) |
| { |
| parent_window = window; |
| } |
| |
| static struct cursor_stack { |
| struct cursor_stack *next; |
| GdkCursor *cursor; |
| } *cursor_stack; |
| |
| static void push_cursor(GdkCursor *cursor) |
| { |
| struct cursor_stack *item; |
| |
| item = malloc_or_die(sizeof(item)); |
| item->next = cursor_stack; |
| cursor_stack = item; |
| item->cursor = cursor; |
| } |
| |
| static GdkCursor *pop_cursor(void) |
| { |
| struct cursor_stack *item; |
| GdkCursor *cursor; |
| |
| item = cursor_stack; |
| if (!item) |
| return NULL; |
| |
| cursor_stack = item->next; |
| cursor = item->cursor; |
| free(item); |
| return cursor; |
| } |
| |
| void trace_set_cursor(GdkCursorType type) |
| { |
| GdkWindow *window; |
| GdkCursor *cursor; |
| |
| if (!parent_window) |
| return; |
| |
| window = GTK_WIDGET(parent_window)->window; |
| |
| /* save the previous cursor */ |
| cursor = gdk_window_get_cursor(window); |
| push_cursor(cursor); |
| |
| cursor = gdk_cursor_new(type); |
| if (!cursor) |
| die("Can't create cursor"); |
| gdk_window_set_cursor(window, cursor); |
| } |
| |
| void trace_put_cursor(void) |
| { |
| GdkWindow *window; |
| GdkCursor *cursor; |
| |
| if (!parent_window) |
| return; |
| |
| window = GTK_WIDGET(parent_window)->window; |
| cursor = gdk_window_get_cursor(window); |
| if (cursor) |
| gdk_cursor_unref(cursor); |
| |
| cursor = pop_cursor(); |
| gdk_window_set_cursor(window, cursor); |
| } |
| |
| void trace_freeze_all(void) |
| { |
| if (parent_window) |
| gtk_widget_set_sensitive(GTK_WIDGET(parent_window), FALSE); |
| } |
| |
| void trace_unfreeze_all(void) |
| { |
| if (parent_window) |
| gtk_widget_set_sensitive(GTK_WIDGET(parent_window), TRUE); |
| } |
| |
| /** |
| * trace_dialog_register_alt_warning - register an alternate function for warning() |
| * @alt: the function to be called instead of warning. |
| * |
| * Add an alternate warning function to be called instead of a popup. |
| * To go back to the popup, simply call this again with NULL. |
| */ |
| void trace_dialog_register_alt_warning(void (*alt)(const char *fmt, va_list ap)) |
| { |
| alt_warning = alt; |
| } |
| |
| void warning(const char *fmt, ...) |
| { |
| GString *str; |
| va_list ap; |
| int err; |
| |
| if (alt_warning) { |
| va_start(ap, fmt); |
| alt_warning(fmt, ap); |
| va_end(ap); |
| return; |
| } |
| |
| if (!parent_window) { |
| va_start(ap, fmt); |
| __vwarning(fmt, ap); |
| va_end(ap); |
| return; |
| } |
| |
| err = errno; |
| errno = 0; |
| |
| str = g_string_new(""); |
| |
| va_start(ap, fmt); |
| g_string_vprintf(str, fmt, ap); |
| va_end(ap); |
| |
| g_string_append(str, "\n"); |
| |
| if (err) { |
| g_string_prepend(str, "\n"); |
| g_string_prepend(str, strerror(err)); |
| } |
| |
| errno = 0; |
| |
| trace_dialog(GTK_WINDOW(parent_window), TRACE_GUI_WARNING, |
| str->str); |
| |
| g_string_free(str, TRUE); |
| } |
| |
| static void |
| status_display_clicked (gpointer data) |
| { |
| GtkWidget *dialog; |
| GtkWidget *scrollwin; |
| GtkWidget *viewport; |
| GtkWidget *textview; |
| GtkTextBuffer *buffer; |
| |
| dialog = gtk_dialog_new_with_buttons("Status", |
| NULL, |
| GTK_DIALOG_MODAL, |
| "OK", |
| GTK_RESPONSE_ACCEPT, |
| NULL); |
| |
| scrollwin = gtk_scrolled_window_new(NULL, NULL); |
| gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), |
| GTK_POLICY_AUTOMATIC, |
| GTK_POLICY_AUTOMATIC); |
| gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); |
| gtk_widget_show(scrollwin); |
| |
| viewport = gtk_viewport_new(NULL, NULL); |
| gtk_widget_show(viewport); |
| |
| gtk_container_add(GTK_CONTAINER(scrollwin), viewport); |
| |
| textview = gtk_text_view_new(); |
| buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); |
| gtk_text_buffer_set_text(buffer, statusstr->str, -1); |
| |
| gtk_container_add(GTK_CONTAINER(viewport), textview); |
| gtk_widget_show(textview); |
| |
| gtk_widget_set_size_request(GTK_WIDGET(dialog), |
| DIALOG_WIDTH, DIALOG_HEIGHT); |
| |
| gtk_dialog_run(GTK_DIALOG(dialog)); |
| |
| gtk_widget_destroy(dialog); |
| } |
| |
| static gboolean |
| do_status_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) |
| { |
| static GtkWidget *menu; |
| static GtkWidget *menu_status_display; |
| |
| if (!menu) { |
| menu = gtk_menu_new(); |
| menu_status_display = gtk_menu_item_new_with_label("Display Status"); |
| gtk_widget_show(menu_status_display); |
| gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_status_display); |
| |
| g_signal_connect_swapped (G_OBJECT (menu_status_display), "activate", |
| G_CALLBACK (status_display_clicked), |
| data); |
| } |
| |
| gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, |
| gtk_get_current_event_time()); |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| button_press_status(GtkWidget *widget, GdkEventButton *event, gpointer data) |
| { |
| if (event->button == 1) |
| return do_status_popup(widget, event, data); |
| |
| return FALSE; |
| } |
| |
| GtkWidget *trace_status_bar_new(void) |
| { |
| GtkWidget *eventbox; |
| |
| statusbar = gtk_statusbar_new(); |
| |
| statuspix = gtk_image_new_from_stock(GTK_STOCK_INFO, |
| GTK_ICON_SIZE_SMALL_TOOLBAR); |
| |
| eventbox = gtk_event_box_new(); |
| gtk_container_add(GTK_CONTAINER(eventbox), statuspix); |
| gtk_widget_show(eventbox); |
| |
| gtk_box_pack_end(GTK_BOX(statusbar), eventbox, FALSE, FALSE, 0); |
| |
| if (statusstr) |
| gtk_widget_show(statuspix); |
| |
| gtk_signal_connect(GTK_OBJECT(eventbox), "button_press_event", |
| (GtkSignalFunc) button_press_status, NULL); |
| |
| return statusbar; |
| } |
| |
| void trace_show_help(GtkWidget *window, const gchar *link, GError **error) |
| { |
| #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) |
| trace_dialog(GTK_WINDOW(window), TRACE_GUI_WARNING, |
| "This version of GTK+ does not implement gtk_show_uri.\n" |
| "Please upgrade your GTK and recompile"); |
| #else |
| gtk_show_uri(gtk_widget_get_screen(GTK_WIDGET(window)), |
| link, |
| GDK_CURRENT_TIME, |
| error); |
| #endif |
| } |
| |
| GtkResponseType trace_dialog(GtkWindow *parent, enum trace_dialog_type type, |
| gchar *message, ...) |
| { |
| GtkWidget *dialog; |
| GtkMessageType mtype; |
| GtkButtonsType btype = GTK_BUTTONS_CLOSE; |
| gchar *str; |
| va_list ap; |
| int result; |
| |
| if (!parent) |
| parent = GTK_WINDOW(parent_window); |
| |
| switch (type) { |
| case TRACE_GUI_OTHER: |
| mtype = GTK_MESSAGE_OTHER; |
| break; |
| case TRACE_GUI_INFO: |
| mtype = GTK_MESSAGE_INFO; |
| break; |
| case TRACE_GUI_WARNING: |
| mtype = GTK_MESSAGE_WARNING; |
| break; |
| case TRACE_GUI_ERROR: |
| mtype = GTK_MESSAGE_ERROR; |
| break; |
| case TRACE_GUI_ASK: |
| mtype = GTK_MESSAGE_WARNING; |
| btype = GTK_BUTTONS_YES_NO; |
| break; |
| } |
| |
| va_start(ap, message); |
| str = g_strdup_vprintf(message, ap); |
| va_end(ap); |
| |
| dialog = gtk_message_dialog_new(parent, |
| GTK_DIALOG_DESTROY_WITH_PARENT, |
| mtype, |
| btype, |
| "%s", str); |
| g_free(str); |
| |
| result = gtk_dialog_run(GTK_DIALOG(dialog)); |
| |
| gtk_widget_destroy(dialog); |
| |
| return result; |
| } |
| |
| static void read_raw_events(struct trace_seq *s, |
| struct event_format *event, |
| struct pevent_record *record) |
| { |
| struct format_field **fields; |
| int i; |
| |
| fields = pevent_event_fields(event); |
| if (!fields) |
| return; |
| |
| for (i = 0; fields[i]; i++) { |
| trace_seq_printf(s, "%s: ", fields[i]->name); |
| pevent_print_field(s, record->data, fields[i]); |
| trace_seq_putc(s, '\n'); |
| } |
| |
| free(fields); |
| } |
| |
| void trace_show_record_dialog(GtkWindow *parent, struct pevent *pevent, |
| struct pevent_record *record, gboolean raw) |
| { |
| struct event_format *event; |
| struct trace_seq s; |
| int type; |
| |
| trace_seq_init(&s); |
| |
| type = pevent_data_type(pevent, record); |
| event = pevent_data_event_from_type(pevent, type); |
| |
| if (raw) |
| read_raw_events(&s, event, record); |
| else |
| pevent_print_event(pevent, &s, record, FALSE); |
| |
| if (s.buffer_size) { |
| trace_seq_terminate(&s); |
| trace_dialog(parent, TRACE_GUI_OTHER, s.buffer); |
| } |
| |
| trace_seq_destroy(&s); |
| } |
| |
| /** |
| * trace_get_file_dialog - pop up a file dialog to get a file |
| * @title: the title of the dialog |
| * @open: the text for the "open" button (NULL for default) |
| * @ftype: What extension the dialog should default filter on. |
| * @warn: if the file exists, warn and let them choose again. |
| * |
| * Returns: the filename if it should be used. NULL otherwise. |
| * The filename needs to be freed with g_free(). |
| */ |
| gchar *trace_get_file_dialog_filter(const gchar *title, const char *open, |
| enum trace_dialog_filter ftype, gboolean warn) |
| { |
| struct stat st; |
| GtkWidget *dialog; |
| GtkResponseType ret; |
| GtkFileFilter *filter; |
| GtkFileFilter *setfilter; |
| gchar *filename = NULL; |
| gchar *ext = NULL; |
| |
| if (!open) |
| open = GTK_STOCK_OPEN; |
| |
| dialog = gtk_file_chooser_dialog_new(title, |
| NULL, |
| GTK_FILE_CHOOSER_ACTION_OPEN, |
| GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
| open, GTK_RESPONSE_ACCEPT, |
| NULL); |
| |
| setfilter = filter = gtk_file_filter_new(); |
| gtk_file_filter_set_name(filter, "All Files"); |
| gtk_file_filter_add_pattern(filter, "*"); |
| gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); |
| |
| filter = gtk_file_filter_new(); |
| gtk_file_filter_set_name(filter, "trace-cmd .dat files"); |
| gtk_file_filter_add_pattern(filter, "*.dat"); |
| gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); |
| |
| if (ftype == TRACE_DIALOG_FILTER_DATA) { |
| setfilter = filter; |
| ext = ".dat"; |
| } |
| |
| filter = gtk_file_filter_new(); |
| gtk_file_filter_set_name(filter, "KernelShark filter files"); |
| gtk_file_filter_add_pattern(filter, "*.ksf"); |
| gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); |
| |
| if (ftype == TRACE_DIALOG_FILTER_FILTER) { |
| setfilter = filter; |
| ext = ".ksf"; |
| } |
| |
| filter = gtk_file_filter_new(); |
| gtk_file_filter_set_name(filter, "KernelShark setting files"); |
| gtk_file_filter_add_pattern(filter, "*.kss"); |
| gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); |
| |
| if (ftype == TRACE_DIALOG_FILTER_SETTING) { |
| setfilter = filter; |
| ext = ".kss"; |
| } |
| |
| gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), setfilter); |
| |
| again: |
| ret = gtk_dialog_run(GTK_DIALOG(dialog)); |
| |
| if (ret == GTK_RESPONSE_ACCEPT) { |
| filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); |
| if (filename && warn) { |
| if (ext) { |
| int len = strlen(filename); |
| gchar *tmp; |
| |
| /* Add extension if not already there */ |
| if (strcmp(filename + (len - 4), ext) != 0) { |
| tmp = filename; |
| filename = g_strdup_printf("%s%s", |
| tmp, ext); |
| g_free(tmp); |
| } |
| } |
| if (stat(filename, &st) >= 0) { |
| ret = trace_dialog(GTK_WINDOW(dialog), TRACE_GUI_ASK, |
| "The file '%s' already exists.\n" |
| "Are you sure you want to replace it", |
| filename); |
| if (ret == GTK_RESPONSE_NO) { |
| g_free(filename); |
| filename = NULL; |
| goto again; |
| } |
| } |
| } |
| } |
| |
| gtk_widget_destroy(dialog); |
| |
| return filename; |
| } |
| |
| gchar *trace_get_file_dialog(const gchar *title, const char *open, |
| gboolean warn) |
| { |
| return trace_get_file_dialog_filter(title, open, TRACE_DIALOG_FILTER_NONE, warn); |
| } |
| |
| /** |
| * trace_create_combo_box - helper function to create a label and combo box |
| * @hbox: The hbox to add the label and combo box to |
| * @text: The text of the label |
| * @combo_model_create: The function used to create the combo model |
| * @data: data to pass to the combo_model_create. |
| * |
| * If no @hbox is given, the @text is ignored, and only the combo box |
| * is created. |
| * |
| * Returns the combo box in the hbox. |
| */ |
| GtkWidget * |
| trace_create_combo_box(GtkWidget *hbox, const gchar *text, |
| GtkTreeModel *(*combo_model_create)(gpointer data), |
| gpointer data) |
| { |
| GtkCellRenderer *renderer; |
| GtkTreeModel *model; |
| GtkWidget *label; |
| GtkWidget *combo; |
| |
| if (hbox) { |
| label = gtk_label_new(text); |
| gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
| gtk_widget_show(label); |
| } |
| |
| /* --- Set up the selection combo box --- */ |
| |
| model = combo_model_create(data); |
| |
| renderer = gtk_cell_renderer_text_new(); |
| |
| combo = gtk_combo_box_new_with_model(model); |
| if (hbox) |
| gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0); |
| gtk_widget_show(combo); |
| |
| /* Free model with combobox */ |
| g_object_unref(model); |
| |
| gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), |
| renderer, |
| TRUE); |
| gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), |
| renderer, |
| "text", 0, |
| NULL); |
| |
| gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); |
| |
| return combo; |
| } |