/*****************************************************************************/
/* Klavaro - a flexible touch typing tutor */
/* Copyright (C) 2005, 2006, 2007, 2008 Felipe Castro */
/* Copyright (C) 2009, 2010, 2011 Free Software Foundation */
/* */
/* This program is free software, licensed under the terms of the GNU */
/* General Public License as published by the Free Software Foundation, */
/* either version 3 of the License, or (at your option) any later version. */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see . */
/*****************************************************************************/
/*
* Charts management
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "auxiliar.h"
#include "main.h"
#include "callbacks.h"
#include "tutor.h"
#include "basic.h"
#include "accuracy.h"
#include "plot.h"
static struct
{
GtkWidget *databox;
GtkWidget *table;
struct
{
gfloat x[DATA_POINTS+1];
gfloat y[DATA_POINTS+1];
} data;
GtkDataboxGraph *point_kernel;
GtkDataboxGraph *point_frame;
GtkDataboxGraph *line_kernel;
GtkDataboxGraph *line_frame;
GtkDataboxGraph *line_outter;
struct
{
gfloat x[1];
gfloat y[1];
} mark;
GtkDataboxGraph *point_marker;
struct
{
gfloat x[2];
gfloat y[2];
} goal;
GtkDataboxGraph *line_goal;
GtkDataboxGraph *grid;
struct
{
gfloat x[2];
gfloat y[2];
} lim;
GtkDataboxGraph *limits;
} plot;
gfloat accur[DATA_POINTS+1];
gfloat velo[DATA_POINTS+1];
gfloat fluid[DATA_POINTS+1];
gfloat score[DATA_POINTS+1];
gchar date[DATA_POINTS+1][20];
gchar hour[DATA_POINTS+1][20];
gint nchars[DATA_POINTS+1];
gchar lesson[DATA_POINTS+1][299];
glong n_points;
gint plot_type;
/*******************************************************************************
* Interface functions
*/
GtkWidget *
plot_get_databox ()
{
return plot.databox;
}
/*******************************************************************************
* Private functions
static void
plot_clip_data (gint i)
{
if (accur[i] > 100)
accur[i] = 100.1;
if (accur[i] < 60)
accur[i] = 59.9;
if (velo[i] > 100)
velo[i] = 100.1;
if (velo[i] < 0)
velo[i] = -0.1;
if (fluid[i] > 100)
fluid[i] = 100.1;
if (fluid[i] < 0)
fluid[i] = -0.1;
if (score[i] < 0)
score[i] = -0.1;
}
*/
static void
plot_error_frequencies ()
{
gint i;
GdkColor color, color2;
GdkColor color_black;
GtkDatabox *box;
box = GTK_DATABOX (plot.databox);
n_points = accur_n_get ();
if (n_points < 1)
return;
accur_sort ();
for (i = 0; i < DATA_POINTS; i++)
{
if (i < n_points)
plot.data.y[i] = accur_wrong_get (i);
else
plot.data.y[i] = 0;
}
/* Format the chart
*/
plot.mark.x[0] = -7;
plot.mark.y[0] = -7;
plot.lim.x[0] = 0;
plot.lim.x[1] = DATA_POINTS + 2;
plot.lim.y[0] = 0;
plot.lim.y[1] = 1.05 * accur_wrong_get (0);
if (plot.lim.y[1] < 1)
plot.lim.y[1] = 1.05;
/* White background
*/
gdk_color_parse ("#ffffff", &color);
gtk_widget_modify_bg (plot.databox, GTK_STATE_NORMAL, &color);
gdk_color_parse ("#000000", &color_black);
gdk_color_parse (PLOT_GREEN_2, &color);
gdk_color_parse (PLOT_RED_2, &color2);
/* Point limits */
plot.limits = gtk_databox_points_new (2, plot.lim.x, plot.lim.y, &color_black, 1);
gtk_databox_graph_add (box, plot.limits);
gtk_databox_auto_rescale (box, 0.0);
/* Bar kernel
*/
plot.point_kernel = gtk_databox_bars_new (i, plot.data.x, plot.data.y, &color, 5);
gtk_databox_graph_add (box, plot.point_kernel);
/* Bar frame
*/
plot.point_frame = gtk_databox_bars_new (i, plot.data.x, plot.data.y, &color_black, 7);
gtk_databox_graph_add (box, plot.point_frame);
/* Data marker
*/
plot.point_marker = gtk_databox_points_new (1, plot.mark.x, plot.mark.y, &color_black, 7);
gtk_databox_graph_add (box, plot.point_marker);
/* Redraw the plot
*/
gtk_widget_show_all (plot.table);
}
/*******************************************************************************
* Functions to manage the plottings on the progress window
*/
void
plot_initialize ()
{
static gboolean inited = FALSE;
gint i;
if (inited)
return;
inited = TRUE;
/* Initialize X data
*/
for (i = 0; i < DATA_POINTS; i++)
plot.data.x[i] = i + 1;
/* Data Box
*/
gtk_databox_create_box_with_scrollbars_and_rulers (&plot.databox, &plot.table, FALSE, FALSE, FALSE, FALSE);
gtk_container_add (GTK_CONTAINER (get_wg ("frame_stat")), plot.table);
g_signal_connect (G_OBJECT (plot.databox), "motion_notify_event", G_CALLBACK (on_databox_hovered), NULL);
plot_draw_chart (1);
}
/**********************************************************************
* Plots the statistics
*/
void
plot_draw_chart (gint field)
{
gint i;
gint lesson_n;
gchar *tmp_locale;
gchar *tmp_name;
gchar tmp_str[2000];
FILE *fh;
GdkColor color, color2, color3;
GdkColor color_black;
GtkDatabox *box;
box = GTK_DATABOX (plot.databox);
/* Blank the chart
*/
n_points = 0;
gtk_databox_graph_remove_all (box);
gtk_widget_hide (plot.table);
plot_type = field;
/* Error frequencies
*/
if (field == 6)
{
plot_error_frequencies ();
return;
}
/* Auxiliar variable to track the lesson to be plot
*/
lesson_n = gtk_spin_button_get_value (GTK_SPIN_BUTTON (get_wg ("spinbutton_stat_lesson")));
if (tutor_get_type () == TT_BASIC)
{
gtk_widget_show (get_wg ("label_stat_lesson"));
gtk_widget_show (get_wg ("spinbutton_stat_lesson"));
}
else
{
gtk_widget_hide (get_wg ("label_stat_lesson"));
gtk_widget_hide (get_wg ("spinbutton_stat_lesson"));
}
/* Get the file name
*/
if (field < 4)
tmp_name = g_strconcat (main_path_stats (), G_DIR_SEPARATOR_S "stat_",
tutor_get_type_name (), ".txt", NULL);
else
tmp_name = g_build_filename (main_path_stats (), "scores_fluid.txt", NULL);
/* Open the data file
*/
fh = (FILE *) g_fopen (tmp_name, "r");
if (!fh)
{
g_message ("no data yet, no statistic file:\n%s", tmp_name);
g_free (tmp_name);
return;
}
/* Reads the first line (header)
*/
if (!fgets (tmp_str, 2000, fh))
{
g_message ("no data on the statistic file:\n%s", tmp_name);
g_free (tmp_name);
fclose (fh);
return;
}
/* Changing to "C" locale
*/
tmp_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
if (tmp_locale != NULL)
setlocale (LC_NUMERIC, "C");
/* Reads the first DATA_POINTS points
*/
for (i = 0; i < DATA_POINTS; i++)
plot.data.y[i] = -1000;
i = 0;
while (1)
{
gint itens;
if (field < 4)
itens = fscanf (fh, "%f%f%f%s%s", &accur[i], &velo[i], &fluid[i], date[i],
hour[i]);
else
itens = fscanf (fh, "%f%s%s%i", &score[i], date[i], hour[i], &nchars[i]);
if (itens != 5 && field < 4)
break;
else if (itens != 4 && field == 4)
break;
if (fgets (lesson[i], 299, fh) == NULL)
break;
if (strlen (lesson[i]) == 299)
{
if (field < 4)
g_warning
("file name of custom lesson too long in line %i of %s: "
"the limit is 299 characters.", i, tmp_name);
else
g_warning
("language name too long in line %i of %s: "
"the limit is 299 characters.", i, tmp_name);
}
//plot_clip_data (i);
if (tutor_get_type () == TT_BASIC && g_ascii_strtoll (lesson[i], NULL, 10) != lesson_n)
continue;
switch (field)
{
case 1:
plot.data.y[i] = accur[i];
break;
case 2:
plot.data.y[i] = velo[i];
break;
case 3:
plot.data.y[i] = fluid[i];
break;
case 4:
plot.data.y[i] = score[i];
break;
}
if (++i == DATA_POINTS)
break;
}
/* Reads until the end, keeping the last DATA_POINTS points
*/
while (1)
{
gint itens;
if (field < 4)
itens = fscanf (fh, "%f%f%f%s%s", &accur[i], &velo[i], &fluid[i], date[i],
hour[i]);
else
itens = fscanf (fh, "%f%s%s%i", &score[i], date[i], hour[i], &nchars[i]);
if (itens != 5 && field < 4)
break;
else if (itens != 4 && field == 4)
break;
if (fgets (lesson[i], 299, fh) == NULL)
break;
if (strlen (lesson[i]) == 299)
{
if (field < 4)
g_warning
("file name of custom lesson too long in line %i of %s: "
"the limit is 299 characters.", i, tmp_name);
else
g_warning
("language name too long in line %i of %s: "
"the limit is 299 characters.", i, tmp_name);
}
//plot_clip_data (i);
if (tutor_get_type () == TT_BASIC && g_ascii_strtoll (lesson[i], NULL, 10) != lesson_n)
continue;
for (i = 0; i < DATA_POINTS - 1; i++)
{
plot.data.y[i] = plot.data.y[i + 1];
strcpy (date[i], date[i + 1]);
strcpy (hour[i], hour[i + 1]);
}
strcpy (date[i], date[i + 1]);
strcpy (hour[i], hour[i + 1]);
switch (field)
{
case 1:
plot.data.y[i] = accur[i + 1];
break;
case 2:
plot.data.y[i] = velo[i + 1];
break;
case 3:
plot.data.y[i] = fluid[i + 1];
break;
case 4:
plot.data.y[i] = score[i + 1];
break;
}
i = DATA_POINTS;
}
fclose (fh);
g_free (tmp_name);
/* Coming back to the right locale
*/
if (tmp_locale != NULL)
setlocale (LC_NUMERIC, tmp_locale);
g_free (tmp_locale);
if (i == 0)
{
g_message ("no valid data to plot.");
gdk_color_parse ("#ccccce", &color);
gtk_widget_modify_bg (plot.databox, GTK_STATE_NORMAL, &color);
return;
}
else
{
gdk_color_parse ("#ffffff", &color);
gtk_widget_modify_bg (plot.databox, GTK_STATE_NORMAL, &color);
}
n_points = i;
/* Format the chart
*/
plot.mark.x[0] = -7;
plot.mark.y[0] = -7;
plot.goal.x[0] = plot.lim.x[0] = 0;
plot.goal.x[1] = plot.lim.x[1] = DATA_POINTS + 2;
plot.lim.y[0] = 0;
plot.lim.y[1] = 100;
gdk_color_parse ("#000000", &color_black);
switch (field)
{
case 1:
plot.lim.y[0] = 60;
plot.goal.y[0] = tutor_goal_accuracy ();
gdk_color_parse (PLOT_GREEN, &color);
gdk_color_parse (PLOT_GREEN_2, &color2);
gdk_color_parse (PLOT_GREEN_3, &color3);
break;
case 2:
plot.goal.y[0] = tutor_goal_speed ();
gdk_color_parse (PLOT_RED, &color);
gdk_color_parse (PLOT_RED_2, &color2);
gdk_color_parse (PLOT_RED_3, &color3);
break;
case 3:
plot.goal.y[0] = tutor_goal_fluidity ();
gdk_color_parse (PLOT_BLUE, &color);
gdk_color_parse (PLOT_BLUE_2, &color2);
gdk_color_parse (PLOT_BLUE_3, &color3);
break;
case 4:
plot.lim.y[1] = 10;
plot.goal.y[0] = -1;
gdk_color_parse (PLOT_ORANGE, &color);
gdk_color_parse (PLOT_ORANGE_2, &color2);
gdk_color_parse (PLOT_ORANGE_3, &color3);
}
plot.goal.y[1] = plot.goal.y[0];
/* Point limits */
plot.limits = gtk_databox_points_new (2, plot.lim.x, plot.lim.y, &color_black, 1);
gtk_databox_graph_add (box, plot.limits);
gtk_databox_auto_rescale (box, 0.0);
//gtk_databox_set_total_limits (box, plot.lim.x[0], plot.lim.x[1], plot.lim.y[0], plot.lim.y[1]);
//g_message ("(%f, %f) / (%f, %f)", plot.lim.x[0], plot.lim.x[1], plot.lim.y[0], plot.lim.y[1]);
/* Point kernel */
plot.point_kernel = gtk_databox_points_new (i, plot.data.x, plot.data.y, &color, 3);
gtk_databox_graph_add (box, plot.point_kernel);
/* Point frame */
plot.point_frame = gtk_databox_points_new (i, plot.data.x, plot.data.y, &color_black, 5);
gtk_databox_graph_add (box, plot.point_frame);
/* Data marker */
plot.point_marker = gtk_databox_points_new (1, plot.mark.x, plot.mark.y, &color_black, 7);
gtk_databox_graph_add (box, plot.point_marker);
/* Kernel line */
plot.line_kernel = gtk_databox_lines_new (i, plot.data.x, plot.data.y, &color, 1);
gtk_databox_graph_add (box, plot.line_kernel);
/* Frame line */
plot.line_frame = gtk_databox_lines_new (i, plot.data.x, plot.data.y, &color2, 3);
gtk_databox_graph_add (box, plot.line_frame);
/* Outter line */
plot.line_outter = gtk_databox_lines_new (i, plot.data.x, plot.data.y, &color3, 5);
gtk_databox_graph_add (box, plot.line_outter);
/* Goal limit */
gdk_color_parse ("#999999", &color3);
plot.line_goal = gtk_databox_lines_new (2, plot.goal.x, plot.goal.y, &color3, 1);
gtk_databox_graph_add (box, plot.line_goal);
/* Grid */
gdk_color_parse ("#dddddd", &color);
if (field == 1)
plot.grid = gtk_databox_grid_new (3, 3, &color, 1);
else
plot.grid = gtk_databox_grid_new (9, 3, &color, 1);
gtk_databox_graph_add (GTK_DATABOX (plot.databox), plot.grid);
/* Redraw the plot
*/
gtk_widget_show_all (plot.table);
}
void
plot_pointer_update (gdouble x)
{
static glong n_prev = 0;
glong n = 0;
gchar *xstr;
gchar *ystr;
GtkDatabox *box;
gint width = 600;
box = GTK_DATABOX (plot.databox);
gdk_drawable_get_size (GDK_DRAWABLE (gtk_databox_get_backing_pixmap (box)), &width, NULL);
n = rintf (x / width * (DATA_POINTS + 2)) - 1;
if (n == n_prev)
return;
n_prev = n;
if (n < 0 || n >= n_points || n >= DATA_POINTS)
{
xstr = g_strdup ("");
ystr = g_strdup ("");
plot.mark.x[0] = -7;
plot.mark.y[0] = -7;
}
else
{
if (plot_type < 6)
{
xstr = g_strdup_printf ("%s - %s", date[n], hour[n]);
ystr = g_strdup_printf ("%.2f", plot.data.y[n]);
}
else
{
xstr = accur_char_utf8 (n);
ystr = g_strdup_printf ("%.0f/%.lu", plot.data.y[n], accur_error_total ());
}
plot.mark.x[0] = plot.data.x[n];
plot.mark.y[0] = plot.data.y[n];
}
gtk_entry_set_text (GTK_ENTRY (get_wg ("entry_stat_x")), xstr);
gtk_entry_set_text (GTK_ENTRY (get_wg ("entry_stat_y")), ystr);
gtk_widget_queue_draw (plot.databox);
g_free (xstr);
g_free (ystr);
}