blob: 5d3aabe9cc0a6b64a3ce1f3f3128c513e3f66c37 [file] [log] [blame]
/*
* 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"
#include "trace-local.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;
}