X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=gfio.c;h=476435217593b88695667b406831c602251533ac;hp=f3e69ef6c45b70a468106f0e086b9c19c301ed6c;hb=c1970b907ff0cb819de5607bc120d8acb527eb98;hpb=45913d20e3bf84b2e624fd0844c90dce2ec58db3 diff --git a/gfio.c b/gfio.c index f3e69ef6..47643521 100644 --- a/gfio.c +++ b/gfio.c @@ -88,6 +88,11 @@ struct gui { GtkWidget *textview; GtkWidget *error_info_bar; GtkWidget *error_label; + GtkWidget *results_notebook; + GtkWidget *results_window; + GtkListStore *log_model; + GtkWidget *log_tree; + GtkWidget *log_view; GtkTextBuffer *text; struct probe_widget probe; struct eta_widget eta; @@ -99,6 +104,12 @@ struct gui { char **job_files; } ui; +struct gfio_client { + struct gui *ui; + GtkWidget *results_widget; + GtkWidget *disk_util_frame; +}; + static void clear_ui_info(struct gui *ui) { gtk_label_set_text(GTK_LABEL(ui->probe.hostname), ""); @@ -165,6 +176,7 @@ static void gfio_set_connected(struct gui *ui, int connected) gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1); ui->connected = 1; gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect"); + gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1); } else { ui->connected = 0; gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect"); @@ -233,6 +245,31 @@ GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char return col; } +static void gfio_ui_setup_log(struct gui *ui) +{ + GtkTreeSelection *selection; + GtkListStore *model; + GtkWidget *tree_view; + + model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING); + + tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_widget_set_can_focus(tree_view, FALSE); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); + gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); + g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, + "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); + + tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE); + tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE); + tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE); + tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE); + + ui->log_model = model; + ui->log_tree = tree_view; +} + static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, fio_fp64_t *plist, unsigned int len, @@ -254,6 +291,9 @@ static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_widget_set_can_focus(tree_view, FALSE); + g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, + "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); @@ -266,8 +306,11 @@ static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, gtk_list_store_append(model, &iter); - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) { + if (scale) + ovals[i] = (ovals[i] + 999) / 1000; gtk_list_store_set(model, &iter, i, ovals[i], -1); + } return tree_view; } @@ -360,10 +403,10 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs, { const char *ddir_label[2] = { "Read", "Write" }; GtkWidget *frame, *label, *box, *vbox, *main_vbox; - unsigned long min, max, runt; + unsigned long min[3], max[3], runt; unsigned long long bw, iops; unsigned int flags = 0; - double mean, dev; + double mean[3], dev[3]; char *io_p, *bw_p, *iops_p; int i2p; @@ -401,22 +444,22 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs, label = new_info_label_in_frame(box, "Runtime (msec)"); label_set_int_value(label, ts->runtime[ddir]); - if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) { + if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) { double p_of_agg = 100.0; const char *bw_str = "KB"; char tmp[32]; if (rs->agg[ddir]) { - p_of_agg = mean * 100 / (double) rs->agg[ddir]; + p_of_agg = mean[0] * 100 / (double) rs->agg[ddir]; if (p_of_agg > 100.0) p_of_agg = 100.0; } - if (mean > 999999.9) { - min /= 1000.0; - max /= 1000.0; - mean /= 1000.0; - dev /= 1000.0; + if (mean[0] > 999999.9) { + min[0] /= 1000.0; + max[0] /= 1000.0; + mean[0] /= 1000.0; + dev[0] /= 1000.0; bw_str = "MB"; } @@ -428,25 +471,25 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs, gtk_container_add(GTK_CONTAINER(frame), box); label = new_info_label_in_frame(box, "Minimum"); - label_set_int_value(label, min); + label_set_int_value(label, min[0]); label = new_info_label_in_frame(box, "Maximum"); - label_set_int_value(label, max); + label_set_int_value(label, max[0]); label = new_info_label_in_frame(box, "Percentage of jobs"); sprintf(tmp, "%3.2f%%", p_of_agg); gtk_label_set_text(GTK_LABEL(label), tmp); label = new_info_label_in_frame(box, "Average"); - sprintf(tmp, "%5.02f", mean); + sprintf(tmp, "%5.02f", mean[0]); gtk_label_set_text(GTK_LABEL(label), tmp); label = new_info_label_in_frame(box, "Standard deviation"); - sprintf(tmp, "%5.02f", dev); + sprintf(tmp, "%5.02f", dev[0]); gtk_label_set_text(GTK_LABEL(label), tmp); } - if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) + if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) flags |= GFIO_SLAT; - if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev)) + if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1])) flags |= GFIO_CLAT; - if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev)) + if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2])) flags |= GFIO_LAT; if (flags) { @@ -457,11 +500,11 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs, gtk_container_add(GTK_CONTAINER(frame), vbox); if (flags & GFIO_SLAT) - gfio_show_lat(vbox, "Submission latency", min, max, mean, dev); + gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]); if (flags & GFIO_CLAT) - gfio_show_lat(vbox, "Completion latency", min, max, mean, dev); + gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]); if (flags & GFIO_LAT) - gfio_show_lat(vbox, "Total latency", min, max, mean, dev); + gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]); } if (ts->clat_percentiles) @@ -505,6 +548,9 @@ static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num, tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_widget_set_can_focus(tree_view, FALSE); + g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, + "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); @@ -687,6 +733,9 @@ static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts) tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_widget_set_can_focus(tree_view, FALSE); + g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, + "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); @@ -700,25 +749,55 @@ static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts) gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3); } +static gboolean results_window_delete(GtkWidget *w, gpointer data) +{ + struct gui *ui = (struct gui *) data; + + gtk_widget_destroy(w); + ui->results_window = NULL; + ui->results_notebook = NULL; + return TRUE; +} + +static GtkWidget *get_results_window(struct gui *ui) +{ + GtkWidget *win, *notebook; + + if (ui->results_window) + return ui->results_notebook; + + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(win), "Results"); + g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui); + g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui); + + notebook = gtk_notebook_new(); + gtk_container_add(GTK_CONTAINER(win), notebook); + + ui->results_window = win; + ui->results_notebook = notebook; + return ui->results_notebook; +} + static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, struct group_run_stats *rs) { - GtkWidget *win, *box, *vbox, *entry; - struct gui *ui = client->client_data; + GtkWidget *res_win, *box, *vbox, *entry; + struct gfio_client *gc = client->client_data; gdk_threads_enter(); - win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - - g_signal_connect(win, "delete-event", G_CALLBACK(gtk_widget_destroy), win); - g_signal_connect(win, "destroy", G_CALLBACK(gtk_widget_destroy), win); + res_win = get_results_window(gc->ui); vbox = gtk_vbox_new(FALSE, 3); - gtk_container_add(GTK_CONTAINER(win), vbox); box = gtk_hbox_new(TRUE, 3); gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); + gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name)); + + gc->results_widget = vbox; + entry = new_info_entry_in_frame(box, "Name"); gtk_entry_set_text(GTK_ENTRY(entry), ts->name); if (strlen(ts->description)) { @@ -743,33 +822,99 @@ static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, gfio_show_cpu_usage(vbox, ts); gfio_show_io_depths(vbox, ts); - gtk_widget_show_all(win); + gtk_widget_show_all(gc->ui->results_window); gdk_threads_leave(); } static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd) { -#if 0 - GtkTextBuffer *buffer; - GtkTextIter end; + struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload; + struct gfio_client *gc = client->client_data; + GtkTreeIter iter; + struct tm *tm; + time_t sec; + char tmp[64], timebuf[80]; + + sec = p->log_sec; + tm = localtime(&sec); + strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm); + sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000); - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview)); gdk_threads_enter(); - gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert(buffer, &end, buf, -1); + + gtk_list_store_append(gc->ui->log_model, &iter); + gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1); + gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1); + gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1); + gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1); + gdk_threads_leave(); - gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview), - &end, 0.0, FALSE, 0.0,0.0); -#else - fio_client_ops.text_op(client, cmd); -#endif } static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd) { + struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload; + struct gfio_client *gc = client->client_data; + GtkWidget *box, *frame, *entry, *vbox; + gdk_threads_enter(); - printf("gfio_disk_util_op called\n"); - fio_client_ops.disk_util(client, cmd); + + if (!gc->results_widget) { + printf("no results!\n"); + goto out; + } + + if (!gc->disk_util_frame) { + gc->disk_util_frame = gtk_frame_new("Disk utilization"); + gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5); + } + + vbox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox); + + frame = gtk_frame_new((char *) p->dus.name); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2); + + box = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), box); + + frame = gtk_frame_new("Read"); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); + vbox = gtk_hbox_new(TRUE, 3); + gtk_container_add(GTK_CONTAINER(frame), vbox); + entry = new_info_entry_in_frame(vbox, "IOs"); + entry_set_int_value(entry, p->dus.ios[0]); + entry = new_info_entry_in_frame(vbox, "Merges"); + entry_set_int_value(entry, p->dus.merges[0]); + entry = new_info_entry_in_frame(vbox, "Sectors"); + entry_set_int_value(entry, p->dus.sectors[0]); + entry = new_info_entry_in_frame(vbox, "Ticks"); + entry_set_int_value(entry, p->dus.ticks[0]); + + frame = gtk_frame_new("Write"); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); + vbox = gtk_hbox_new(TRUE, 3); + gtk_container_add(GTK_CONTAINER(frame), vbox); + entry = new_info_entry_in_frame(vbox, "IOs"); + entry_set_int_value(entry, p->dus.ios[1]); + entry = new_info_entry_in_frame(vbox, "Merges"); + entry_set_int_value(entry, p->dus.merges[1]); + entry = new_info_entry_in_frame(vbox, "Sectors"); + entry_set_int_value(entry, p->dus.sectors[1]); + entry = new_info_entry_in_frame(vbox, "Ticks"); + entry_set_int_value(entry, p->dus.ticks[1]); + + frame = gtk_frame_new("Shared"); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); + vbox = gtk_hbox_new(TRUE, 3); + gtk_container_add(GTK_CONTAINER(frame), vbox); + entry = new_info_entry_in_frame(vbox, "IO ticks"); + entry_set_int_value(entry, p->dus.io_ticks); + entry = new_info_entry_in_frame(vbox, "Time in queue"); + entry_set_int_value(entry, p->dus.time_in_queue); + + gtk_widget_show_all(gc->results_widget); +out: gdk_threads_leave(); } @@ -896,6 +1041,8 @@ static void gfio_update_eta(struct jobs_eta *je) static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) { struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload; + struct gfio_client *gc = client->client_data; + struct gui *ui = gc->ui; const char *os, *arch; char buf[64]; @@ -912,11 +1059,13 @@ static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) gdk_threads_enter(); - gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname); - gtk_label_set_text(GTK_LABEL(ui.probe.os), os); - gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch); + gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname); + gtk_label_set_text(GTK_LABEL(ui->probe.os), os); + gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch); sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch); - gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf); + gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf); + + gfio_set_connected(ui, 1); gdk_threads_leave(); } @@ -936,17 +1085,18 @@ static void gfio_update_thread_status(char *status_message, double perc) static void gfio_quit_op(struct fio_client *client) { - struct gui *ui = client->client_data; + struct gfio_client *gc = client->client_data; gdk_threads_enter(); - gfio_set_connected(ui, 0); + gfio_set_connected(gc->ui, 0); gdk_threads_leave(); } static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd) { struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload; - struct gui *ui = client->client_data; + struct gfio_client *gc = client->client_data; + struct gui *ui = gc->ui; char tmp[8]; int i; @@ -975,19 +1125,19 @@ static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd) static void gfio_client_timed_out(struct fio_client *client) { - struct gui *ui = client->client_data; + struct gfio_client *gc = client->client_data; GtkWidget *dialog, *label, *content; char buf[256]; gdk_threads_enter(); - gfio_set_connected(ui, 0); - clear_ui_info(ui); + gfio_set_connected(gc->ui, 0); + clear_ui_info(gc->ui); sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname); dialog = gtk_dialog_new_with_buttons("Timed out!", - GTK_WINDOW(ui->window), + GTK_WINDOW(gc->ui->window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); @@ -1077,9 +1227,10 @@ static void connect_clicked(GtkWidget *widget, gpointer data) if (!ui->nr_job_files) file_open(widget, data); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running"); - fio_clients_connect(); - pthread_create(&ui->t, NULL, job_thread, NULL); - gfio_set_connected(ui, 1); + if (!fio_clients_connect()) { + pthread_create(&ui->t, NULL, job_thread, NULL); + gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0); + } } else { fio_clients_terminate(); gfio_set_connected(ui, 0); @@ -1182,10 +1333,10 @@ static int get_connection_details(char **host, int *port, int *type, hbox = gtk_hbox_new(TRUE, 4); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); - combo = gtk_combo_box_text_new(); - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4"); - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6"); - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket"); + combo = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket"); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); gtk_container_add(GTK_CONTAINER(hbox), combo); @@ -1213,7 +1364,7 @@ static int get_connection_details(char **host, int *port, int *type, *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry))); *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry)); - typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo)); + typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo)); if (!typeentry || !strncmp(typeentry, "IPv4", 4)) *type = Fio_client_ipv4; else if (!strncmp(typeentry, "IPv6", 4)) @@ -1228,6 +1379,17 @@ static int get_connection_details(char **host, int *port, int *type, return 0; } +static void gfio_client_added(struct gui *ui, struct fio_client *client) +{ + struct gfio_client *gc; + + gc = malloc(sizeof(*gc)); + memset(gc, 0, sizeof(*gc)); + gc->ui = ui; + + client->client_data = gc; +} + static void file_open(GtkWidget *w, gpointer data) { GtkWidget *dialog; @@ -1265,12 +1427,14 @@ static void file_open(GtkWidget *w, gpointer data) filenames = fn_glist; while (filenames != NULL) { + struct fio_client *client; + ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *)); ui.job_files[ui.nr_job_files] = strdup(filenames->data); ui.nr_job_files++; - ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port); - if (!ui.client) { + client = fio_client_add_explicit(&gfio_client_ops, host, type, port); + if (!client) { GError *error; error = g_error_new(g_quark_from_string("fio"), 1, @@ -1278,7 +1442,7 @@ static void file_open(GtkWidget *w, gpointer data) report_error(error); g_error_free(error); } - ui.client->client_data = &ui; + gfio_client_added(&ui, client); g_free(filenames->data); filenames = g_slist_next(filenames); @@ -1312,6 +1476,46 @@ static void file_save(GtkWidget *w, gpointer data) gtk_widget_destroy(dialog); } +static void view_log_destroy(GtkWidget *w, gpointer data) +{ + struct gui *ui = (struct gui *) data; + + gtk_widget_ref(ui->log_tree); + gtk_container_remove(GTK_CONTAINER(w), ui->log_tree); + gtk_widget_destroy(w); + ui->log_view = NULL; +} + +static void view_log(GtkWidget *w, gpointer data) +{ + GtkWidget *win, *scroll, *vbox, *box; + struct gui *ui = (struct gui *) data; + + if (ui->log_view) + return; + + ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(win), "Log"); + gtk_window_set_default_size(GTK_WINDOW(win), 700, 500); + + scroll = gtk_scrolled_window_new(NULL, NULL); + + gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + box = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree); + g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box); + + vbox = gtk_vbox_new(TRUE, 5); + gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll); + + gtk_container_add(GTK_CONTAINER(win), vbox); + gtk_widget_show_all(win); +} + static void preferences(GtkWidget *w, gpointer data) { GtkWidget *dialog, *frame, *box, **buttons; @@ -1371,10 +1575,12 @@ static void about_dialog(GtkWidget *w, gpointer data) static GtkActionEntry menu_items[] = { { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, + { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL}, { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL}, { "OpenFile", GTK_STOCK_OPEN, NULL, "O", NULL, G_CALLBACK(file_open) }, { "SaveFile", GTK_STOCK_SAVE, NULL, "S", NULL, G_CALLBACK(file_save) }, { "Preferences", GTK_STOCK_PREFERENCES, NULL, "p", NULL, G_CALLBACK(preferences) }, + { "ViewLog", NULL, "Log", "l", NULL, G_CALLBACK(view_log) }, { "Quit", GTK_STOCK_QUIT, NULL, "Q", NULL, G_CALLBACK(quit_clicked) }, { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, }; @@ -1391,6 +1597,9 @@ static const gchar *ui_string = " \ \ \ \ + \ + \ + \ \ \ \ @@ -1398,13 +1607,14 @@ static const gchar *ui_string = " \ \ "; -static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager) +static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager, + struct gui *ui) { GtkActionGroup *action_group = gtk_action_group_new("Menu"); GError *error = 0; action_group = gtk_action_group_new("Menu"); - gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0); + gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui); gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error); @@ -1451,7 +1661,7 @@ static void init_ui(int *argc, char **argv[], struct gui *ui) gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox); uimanager = gtk_ui_manager_new(); - menu = get_menubar_menu(ui->window, uimanager); + menu = get_menubar_menu(ui->window, uimanager, ui); gfio_ui_setup(settings, menu, ui->vbox, uimanager); /* @@ -1541,6 +1751,7 @@ static void init_ui(int *argc, char **argv[], struct gui *ui) gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections"); gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb); + gfio_ui_setup_log(ui); gtk_widget_show_all(ui->window); }