"Fossies" - the Fresh Open Source Software Archive

Member "klavaro-3.13/src/plot.c" (18 Apr 2021, 18634 Bytes) of package /linux/privat/klavaro-3.13.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "plot.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.12_vs_3.13.

    1 /**************************************************************************/
    2 /*  Klavaro - a flexible touch typing tutor                               */
    3 /*  Copyright (C) 2005-2021 Felipe Emmanuel Ferreira de Castro            */
    4 /*                                                                        */
    5 /*  This file is part of Klavaro, which is a free software: you can       */
    6 /*  redistribute it and/or modify it under the terms of the GNU General   */
    7 /*  Public License as published by the Free Software Foundation, either   */
    8 /*  version 3 of the License, or (at your option) any later version.      */
    9 /*                                                                        */
   10 /*  Klavaro is distributed in the hope that it will be useful,            */
   11 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of        */
   12 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
   13 /*  GNU General Public License for more details (in the file COPYING).    */
   14 /*  You should have received a copy of the GNU General Public License     */
   15 /*  along with Klavaro.  If not, see <https://www.gnu.org/licenses/>      */
   16 /**************************************************************************/
   17 
   18 /*
   19  * Charts management
   20  */
   21 #include <math.h>
   22 #include <stdio.h>
   23 #include <string.h>
   24 #include <locale.h>
   25 #include <glib.h>
   26 #include <glib/gstdio.h>
   27 #include <gdk/gdk.h>
   28 #include <gtk/gtk.h>
   29 #include <gtkdatabox.h>
   30 #include <gtkdatabox_grid.h>
   31 #include <gtkdatabox_lines.h>
   32 #include <gtkdatabox_points.h>
   33 #include <gtkdatabox_bars.h>
   34 
   35 #include "auxiliar.h"
   36 #include "main.h"
   37 #include "callbacks.h"
   38 #include "tutor.h"
   39 #include "basic.h"
   40 #include "accuracy.h"
   41 #include "keyboard.h"
   42 #include "translation.h"
   43 #include "plot.h"
   44 
   45 static struct
   46 {
   47     GtkWidget *databox;
   48     GtkWidget *gtkgrid;
   49 
   50     struct
   51     {
   52         gfloat x[DATA_POINTS+1];
   53         gfloat y[DATA_POINTS+1];
   54     } data;
   55     GtkDataboxGraph *point_kernel;
   56     GtkDataboxGraph *point_frame;
   57     GtkDataboxGraph *line_kernel;
   58     GtkDataboxGraph *line_frame;
   59     GtkDataboxGraph *line_outter;
   60 
   61     struct
   62     {
   63         gfloat x[1];
   64         gfloat y[1];
   65     } mark;
   66     GtkDataboxGraph *point_marker;
   67 
   68     struct
   69     {
   70         gfloat x[2];
   71         gfloat y[2];
   72     } goal;
   73     GtkDataboxGraph *line_goal;
   74     GtkDataboxGraph *grid;
   75     GtkWidget * label_y[MAX_Y_LABELS];
   76 
   77     struct
   78     {
   79         gfloat x[2];
   80         gfloat y[2];
   81     } lim;
   82     GtkDataboxGraph *limits;
   83 } plot;
   84 
   85 gfloat accur[DATA_POINTS+1];
   86 gfloat velo[DATA_POINTS+1];
   87 gfloat fluid[DATA_POINTS+1];
   88 gfloat score[DATA_POINTS+1];
   89 gchar date[DATA_POINTS+1][20];
   90 gchar hour[DATA_POINTS+1][20];
   91 gint nchars[DATA_POINTS+1];
   92 gchar lesson[DATA_POINTS+1][299];
   93 gchar language[DATA_POINTS+1][80+1];
   94 glong n_points;
   95 gint plot_type; /* used to communicate the plotting type, for updating the cursor marker, etc */
   96 
   97 gint lesson_n;
   98 
   99 /*******************************************************************************
  100  * Interface functions
  101  */
  102 GtkWidget *
  103 plot_get_databox ()
  104 {
  105     return plot.databox;
  106 }
  107 
  108 /*******************************************************************************
  109  * Private functions
  110  */
  111 
  112 static void
  113 plot_error_frequencies ()
  114 {
  115     gint i;
  116     gchar *tcolor;
  117     GdkRGBA color;
  118     GdkRGBA color_black;
  119     GtkDatabox *box;
  120 
  121     box = GTK_DATABOX (plot.databox);
  122     n_points = accur_terror_n_get ();
  123     if (n_points < 1)
  124         return;
  125     accur_terror_sort ();
  126     for (i = 0; i < DATA_POINTS; i++)
  127     {
  128         if (i < n_points)
  129             plot.data.y[i] = accur_wrong_get (i);
  130         else
  131             plot.data.y[i] = 0;
  132     }
  133 
  134     /* Format the chart
  135      */
  136     plot.mark.x[0] = -7;
  137     plot.mark.y[0] = -7;
  138     plot.lim.x[0] = 0;
  139     plot.lim.x[1] = DATA_POINTS + 2;
  140     plot.lim.y[0] = 0;
  141     plot.lim.y[1] = 1.05 * accur_wrong_get (0);
  142     if (plot.lim.y[1] < 1)
  143         plot.lim.y[1] = 1.05;
  144 
  145     /* Plot color */
  146     if (main_altcolor_get_boolean ("colors", "altcolor"))
  147         tcolor = main_altcolor_get_string ("colors", "char_untouched_fg");
  148     else
  149         tcolor = main_preferences_get_string ("colors", "char_untouched_fg");
  150     gdk_rgba_parse (&color_black, tcolor);
  151     gdk_rgba_parse (&color, PLOT_GREEN_2);
  152 
  153     /* Point limits */
  154     plot.limits = gtk_databox_points_new (2, plot.lim.x, plot.lim.y, &color_black, 1);
  155     gtk_databox_graph_add (box, plot.limits);
  156     gtk_databox_auto_rescale (box, 0.0);
  157 
  158     /* Bar kernel
  159      */
  160     plot.point_kernel = gtk_databox_bars_new (i, plot.data.x, plot.data.y, &color, 5);
  161     gtk_databox_graph_add (box, plot.point_kernel);
  162 
  163     /* Bar frame
  164      */
  165     plot.point_frame = gtk_databox_bars_new (i, plot.data.x, plot.data.y, &color_black, 7);
  166     gtk_databox_graph_add (box, plot.point_frame);
  167 
  168     /* Data marker
  169      */
  170     plot.point_marker = gtk_databox_points_new (1, plot.mark.x, plot.mark.y, &color_black, 7);
  171     gtk_databox_graph_add (box, plot.point_marker);
  172 
  173     /* Redraw the plot
  174      */
  175     gtk_widget_show_all (plot.gtkgrid);
  176 }
  177 
  178 static void
  179 plot_touch_times ()
  180 {
  181     gint i;
  182     gchar *tcolor;
  183     GdkRGBA color;
  184     GdkRGBA color_black;
  185     GtkDatabox *box;
  186 
  187     box = GTK_DATABOX (plot.databox);
  188     n_points = accur_ttime_n_get ();
  189     if (n_points < 1)
  190         return;
  191     accur_ttime_sort ();
  192     for (i = 0; i < DATA_POINTS; i++)
  193     {
  194         if (i < n_points)
  195             plot.data.y[i] = accur_profi_aver (i);
  196         else
  197             plot.data.y[i] = 0;
  198     }
  199 
  200     /* Format the chart
  201      */
  202     plot.mark.x[0] = -7;
  203     plot.mark.y[0] = -7;
  204     plot.lim.x[0] = 0;
  205     plot.lim.x[1] = DATA_POINTS + 2;
  206     plot.lim.y[0] = 0;
  207     plot.lim.y[1] = 1.05 * accur_profi_aver (0);
  208     if (plot.lim.y[1] < 0.01)
  209         plot.lim.y[1] = 0.0105;
  210 
  211     /* Plot color */
  212     if (main_altcolor_get_boolean ("colors", "altcolor"))
  213         tcolor = main_altcolor_get_string ("colors", "char_untouched_fg");
  214     else
  215         tcolor = main_preferences_get_string ("colors", "char_untouched_fg");
  216     gdk_rgba_parse (&color_black, tcolor);
  217     gdk_rgba_parse (&color, PLOT_PURPLE);
  218 
  219     /* Point limits */
  220     plot.limits = gtk_databox_points_new (2, plot.lim.x, plot.lim.y, &color_black, 1);
  221     gtk_databox_graph_add (box, plot.limits);
  222     gtk_databox_auto_rescale (box, 0.0);
  223 
  224     /* Bar kernel
  225      */
  226     plot.point_kernel = gtk_databox_bars_new (i, plot.data.x, plot.data.y, &color, 5);
  227     gtk_databox_graph_add (box, plot.point_kernel);
  228 
  229     /* Bar frame
  230      */
  231     plot.point_frame = gtk_databox_bars_new (i, plot.data.x, plot.data.y, &color_black, 7);
  232     gtk_databox_graph_add (box, plot.point_frame);
  233 
  234     /* Data marker
  235      */
  236     plot.point_marker = gtk_databox_points_new (1, plot.mark.x, plot.mark.y, &color_black, 7);
  237     gtk_databox_graph_add (box, plot.point_marker);
  238 
  239     /* Redraw the plot
  240      */
  241     gtk_widget_show_all (plot.gtkgrid);
  242 }
  243 
  244 /*******************************************************************************
  245  * Functions to manage the plottings on the progress window
  246  */
  247 void
  248 plot_initialize ()
  249 {
  250     static gboolean inited = FALSE;
  251     static GtkCssProvider *css;
  252     GtkStyleContext *sc;
  253     gchar *tcolor;
  254     gint i;
  255 
  256     if (inited)
  257         return;
  258     inited = TRUE;
  259 
  260     /* Initialize X data
  261      */
  262     for (i = 0; i < DATA_POINTS; i++)
  263         plot.data.x[i] = i + 1;
  264 
  265     /* Data Box
  266      */
  267     gtk_databox_create_box_with_scrollbars_and_rulers (&plot.databox, &plot.gtkgrid, FALSE, FALSE, FALSE, FALSE);
  268     gtk_container_add (GTK_CONTAINER (get_wg ("frame_stat")), plot.gtkgrid);
  269     g_signal_connect (G_OBJECT (plot.databox), "motion_notify_event", G_CALLBACK (on_databox_hovered), NULL);
  270 
  271     if (main_altcolor_get_boolean ("colors", "altcolor"))
  272         tcolor = main_altcolor_get_string ("colors", "text_intro_bg");
  273     else
  274         tcolor = main_preferences_get_string ("colors", "text_intro_bg");
  275     gtk_databox_set_bg_color (GTK_DATABOX(plot.databox), tcolor);
  276 
  277     /* Y labels
  278      */
  279     for (i = 0; i < MAX_Y_LABELS; i++)
  280     {
  281         plot.label_y[i] = gtk_label_new ("???");
  282         gtk_box_pack_start (GTK_BOX (get_wg ("box_grid_label_y")), plot.label_y[i], TRUE, TRUE, 0);
  283     }
  284 
  285     plot_draw_chart (1);
  286 }
  287 
  288 /**********************************************************************
  289  * Plots the statistics
  290  */
  291 void
  292 plot_draw_chart (gint pltype)
  293 {
  294     gint i, len;
  295     gchar *kb_name;
  296     gchar *tmp_name;
  297     gchar *tmp_locale;
  298     gchar *tmp_lang;
  299     gchar tmp_str[2000];
  300     FILE *fh;
  301     gchar *tcolor;
  302     GdkRGBA color, color2, color3;
  303     GdkRGBA color_black;
  304     GtkDatabox *box;
  305 
  306     box = GTK_DATABOX (plot.databox);
  307     /* Blank the chart
  308      */
  309     n_points = 0;
  310     gtk_databox_graph_remove_all (box);
  311     gtk_widget_hide (plot.gtkgrid);
  312 
  313     /* Set plot type for external reference */
  314     plot_type = pltype;
  315 
  316     /* Error frequencies or touch times
  317      */
  318     gtk_widget_set_tooltip_text (get_wg ("entry_stat_x"), _("Character"));
  319     gtk_widget_hide (get_wg ("box_grid_label_y"));
  320     if (pltype == 6)
  321     {
  322         plot_error_frequencies ();
  323         return;
  324     }
  325     else if (pltype == 7)
  326     {
  327         plot_touch_times ();
  328         return;
  329     }
  330     gtk_widget_set_tooltip_text (get_wg ("entry_stat_x"), _("Date & Time"));
  331     gtk_widget_show (get_wg ("box_grid_label_y"));
  332 
  333     /* Auxiliar variable to track the lesson to be plot
  334      */
  335     lesson_n = gtk_spin_button_get_value (GTK_SPIN_BUTTON (get_wg ("spinbutton_stat_lesson")));
  336 
  337     if (tutor_get_type () == TT_BASIC)
  338     {
  339         gtk_widget_show (get_wg ("label_stat_lesson"));
  340         gtk_widget_show (get_wg ("spinbutton_stat_lesson"));
  341     }
  342     else
  343     {
  344         gtk_widget_hide (get_wg ("label_stat_lesson"));
  345         gtk_widget_hide (get_wg ("spinbutton_stat_lesson"));
  346     }
  347 
  348     /* Get the file name
  349      */
  350     if (pltype < 4)
  351         tmp_name = g_strconcat (main_path_stats (), G_DIR_SEPARATOR_S "stat_",
  352                     tutor_get_type_name (), ".txt", NULL);
  353     else
  354         tmp_name = g_build_filename (main_path_stats (), "scores_fluid.txt", NULL);
  355 
  356     /* Open the data file
  357      */
  358     fh = (FILE *) g_fopen (tmp_name, "r");
  359     if (!fh)
  360     {
  361         g_message ("no data yet, no statistic file:\n%s", tmp_name);
  362         g_free (tmp_name);
  363         return;
  364     }
  365     
  366     /* Change to "C" locale, but keep current language
  367      */
  368         tmp_lang = trans_get_current_language ();
  369     tmp_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
  370     if (tmp_locale != NULL)
  371         setlocale (LC_NUMERIC, "C");
  372 
  373     /* Keyboard names must be compared without spaces (may appear in custom files)
  374      */
  375     kb_name = g_strdup (keyb_get_name ());
  376     for (i=0; kb_name[i]; i++)
  377         kb_name[i] = (kb_name[i] == ' ') ? '_' : kb_name[i];
  378 
  379     /* Read the first line (header)
  380      */
  381     if (!fgets (tmp_str, 2000, fh))
  382     {
  383         g_message ("no data on the statistic file:\n%s", tmp_name);
  384         g_free (tmp_name);
  385         fclose (fh);
  386         return;
  387     }
  388     g_free (tmp_name);
  389 
  390     /* Read the first DATA_POINTS points
  391      */
  392     for (i = 0; i < DATA_POINTS; i++)
  393         plot.data.y[i] = -1000;
  394     i = 0;
  395     while (1)
  396     {
  397         gint itens;
  398         gchar *lang_extra;
  399 
  400         language[i][0] = '\0';
  401         if (pltype < 4)
  402         {
  403             itens = fscanf (fh, "%f%f%f%s%s%s\t", &accur[i], &velo[i], &fluid[i],
  404                         date[i], hour[i], lesson[i]);
  405             if (itens != 6) break;
  406         }
  407         else
  408         {
  409             itens = fscanf (fh, "%f%s%s%i\t", &score[i], date[i], hour[i], &nchars[i]);
  410             if (itens != 4) break;
  411         }
  412         lang_extra = fgets (language[i], 80, fh); 
  413         if (language[i][len = (strlen(language[i])-1)] == '\n')
  414             language[i][len] = '\0';
  415 
  416         if (tutor_get_type () == TT_BASIC)
  417         {
  418             if (g_ascii_strtoll (lesson[i], NULL, 10) != lesson_n)
  419                 if (lesson_n != 0)
  420                     continue;
  421             if (strcmp (language[i], kb_name) != 0) // in BASIC, language field is the keyboard.
  422                 continue;
  423         }
  424         if (tutor_get_type () == TT_ADAPT && strcmp (lesson[i], kb_name) != 0)
  425             continue;
  426         if (tutor_get_type () == TT_VELO && strcmp (language[i], tmp_lang) != 0)
  427             continue;
  428         if (tutor_get_type () == TT_FLUID && strcmp (language[i], tmp_lang) != 0)
  429             continue;
  430         switch (pltype)
  431         {
  432         case 1:
  433             plot.data.y[i] = accur[i];
  434             break;
  435         case 2:
  436             plot.data.y[i] = velo[i];
  437             break;
  438         case 3:
  439             plot.data.y[i] = fluid[i];
  440             break;
  441         case 4:
  442             plot.data.y[i] = score[i];
  443             break;
  444         }
  445         if (++i == DATA_POINTS)
  446             break;
  447     }
  448 
  449     /* Read until the end, keeping the last DATA_POINTS points 
  450      */
  451     while (1)
  452     {
  453         gint itens;
  454         gchar *lang_extra;
  455 
  456         language[i][0] = '\0';
  457         if (pltype < 4)
  458         {
  459             itens = fscanf (fh, "%f%f%f%s%s%s\t", &accur[i], &velo[i], &fluid[i],
  460                         date[i], hour[i], lesson[i]);
  461             if (itens != 6) break;
  462         }
  463         else
  464         {
  465             itens = fscanf (fh, "%f%s%s%i\t", &score[i], date[i], hour[i], &nchars[i]);
  466             if (itens != 4) break;
  467         }
  468         lang_extra = fgets (language[i], 80, fh); 
  469         if (language[i][len = (strlen(language[i])-1)] == '\n')
  470             language[i][len] = '\0';
  471 
  472         if (tutor_get_type () == TT_BASIC)
  473         {
  474             if (g_ascii_strtoll (lesson[i], NULL, 10) != lesson_n)
  475                 if (lesson_n != 0)
  476                     continue;
  477             if (strcmp (language[i], kb_name) != 0)
  478                 continue;
  479         }
  480         if (tutor_get_type () == TT_ADAPT && strcmp (lesson[i], kb_name) != 0)
  481             continue;
  482         if (tutor_get_type () == TT_VELO && strcmp (language[i], tmp_lang) != 0)
  483             continue;
  484         if (tutor_get_type () == TT_FLUID && strcmp (language[i], tmp_lang) != 0)
  485             continue;
  486         for (i = 0; i < DATA_POINTS - 1; i++)
  487         {
  488             plot.data.y[i] = plot.data.y[i + 1];
  489             strcpy (date[i], date[i + 1]);
  490             strcpy (hour[i], hour[i + 1]);
  491         }
  492         strcpy (date[i], date[i + 1]);
  493         strcpy (hour[i], hour[i + 1]);
  494 
  495         switch (pltype)
  496         {
  497         case 1:
  498             plot.data.y[i] = accur[i + 1];
  499             break;
  500         case 2:
  501             plot.data.y[i] = velo[i + 1];
  502             break;
  503         case 3:
  504             plot.data.y[i] = fluid[i + 1];
  505             break;
  506         case 4:
  507             plot.data.y[i] = score[i + 1];
  508             break;
  509         }
  510         i = DATA_POINTS;
  511     }
  512     fclose (fh);
  513     g_free (kb_name);
  514 
  515     /* Coming back to the right locale
  516      */
  517     if (tmp_locale != NULL)
  518         setlocale (LC_NUMERIC, tmp_locale);
  519     g_free (tmp_locale);
  520 
  521     if (i == 0)
  522     {
  523         g_message ("no valid data to plot.");
  524         return;
  525     }
  526     n_points = i;
  527 
  528     /* Format the chart
  529      */
  530     plot.mark.x[0] = -7;
  531     plot.mark.y[0] = -7;
  532     plot.goal.x[0] = plot.lim.x[0] = 0;
  533     plot.goal.x[1] = plot.lim.x[1] = DATA_POINTS + 2;
  534     plot.lim.y[0] = 0;
  535     plot.lim.y[1] = 100;
  536      
  537     if (main_altcolor_get_boolean ("colors", "altcolor"))
  538         tcolor = main_altcolor_get_string ("colors", "char_untouched_fg");
  539     else
  540         tcolor = main_preferences_get_string ("colors", "char_untouched_fg");
  541     gdk_rgba_parse (&color_black, tcolor);
  542     switch (pltype)
  543     {
  544     case 1:
  545         plot.lim.y[0] = 60;
  546         plot.goal.y[0] = tutor_goal_accuracy ();
  547         gdk_rgba_parse (&color, PLOT_GREEN);
  548         gdk_rgba_parse (&color2, PLOT_GREEN_2);
  549         gdk_rgba_parse (&color3, PLOT_GREEN_3);
  550         break;
  551     case 2:
  552         plot.lim.y[1] = 120;
  553         plot.goal.y[0] = tutor_goal_speed ();
  554         gdk_rgba_parse (&color, PLOT_RED);
  555         gdk_rgba_parse (&color2, PLOT_RED_2);
  556         gdk_rgba_parse (&color3, PLOT_RED_3);
  557         break;
  558     case 3:
  559         plot.goal.y[0] = tutor_goal_fluidity ();
  560         gdk_rgba_parse (&color, PLOT_BLUE);
  561         gdk_rgba_parse (&color2, PLOT_BLUE_2);
  562         gdk_rgba_parse (&color3, PLOT_BLUE_3);
  563         break;
  564     case 4:
  565         plot.lim.y[1] = 10;
  566         plot.goal.y[0] = -1;
  567         gdk_rgba_parse (&color, PLOT_ORANGE);
  568         gdk_rgba_parse (&color2, PLOT_ORANGE_2);
  569         gdk_rgba_parse (&color3, PLOT_ORANGE_3);
  570     }
  571     plot.goal.y[1] = plot.goal.y[0];
  572 
  573     /* Point limits */
  574     plot.limits = gtk_databox_points_new (2, plot.lim.x, plot.lim.y, &color_black, 1);
  575     gtk_databox_graph_add (box, plot.limits);
  576     gtk_databox_auto_rescale (box, 0.0);
  577 
  578     /* Point kernel */
  579     plot.point_kernel = gtk_databox_points_new (i, plot.data.x, plot.data.y, &color, 3);
  580     gtk_databox_graph_add (box, plot.point_kernel);
  581 
  582     /* Point frame */
  583     plot.point_frame = gtk_databox_points_new (i, plot.data.x, plot.data.y, &color_black, 5);
  584     gtk_databox_graph_add (box, plot.point_frame);
  585 
  586     /* Data marker */
  587     plot.point_marker = gtk_databox_points_new (1, plot.mark.x, plot.mark.y, &color_black, 7);
  588     gtk_databox_graph_add (box, plot.point_marker);
  589 
  590     /* Kernel line */
  591     plot.line_kernel = gtk_databox_lines_new (i, plot.data.x, plot.data.y, &color, 1);
  592     gtk_databox_graph_add (box, plot.line_kernel);
  593 
  594     /* Frame line */
  595     plot.line_frame = gtk_databox_lines_new (i, plot.data.x, plot.data.y, &color2, 3);
  596     gtk_databox_graph_add (box, plot.line_frame);
  597 
  598     /* Outter line */
  599     plot.line_outter = gtk_databox_lines_new (i, plot.data.x, plot.data.y, &color3, 5);
  600     gtk_databox_graph_add (box, plot.line_outter);
  601 
  602     /* Goal limit */
  603     gdk_rgba_parse (&color3, "#808080");
  604     plot.line_goal = gtk_databox_lines_new (2, plot.goal.x, plot.goal.y, &color3, 1);
  605     gtk_databox_graph_add (box, plot.line_goal);
  606 
  607     /* Grid and y labels */
  608     gdk_rgba_parse (&color, "#bbbbbb");
  609     if (pltype == 1) /* Correctness (%) */
  610     {
  611         plot.grid = gtk_databox_grid_new (3, 3, &color, 1);
  612         for (i = 0; i < 5; i++)
  613         {
  614             g_sprintf (tmp_str, "%u", 100 - 10*i);
  615             gtk_label_set_text (GTK_LABEL (plot.label_y[i]), tmp_str);
  616             gtk_label_set_xalign (GTK_LABEL (plot.label_y[i]), 0.9);
  617             gtk_label_set_yalign (GTK_LABEL (plot.label_y[i]), i / 4.0);
  618             gtk_widget_show (plot.label_y[i]);
  619         }
  620         for (; i < MAX_Y_LABELS; i++)
  621             gtk_widget_hide (plot.label_y[i]);
  622     }
  623     else if (pltype == 2) /* Speed (WPM) */
  624     {
  625         plot.grid = gtk_databox_grid_new (11, 3, &color, 1);
  626         for (i = 0; i < 13; i++)
  627         {
  628             g_sprintf (tmp_str, "%u", 120 - 10*i);
  629             gtk_label_set_text (GTK_LABEL (plot.label_y[i]), tmp_str);
  630             gtk_label_set_xalign (GTK_LABEL (plot.label_y[i]), 0.9);
  631             gtk_label_set_yalign (GTK_LABEL (plot.label_y[i]), i / 12.0);
  632             gtk_widget_show (plot.label_y[i]);
  633         }
  634     }
  635     else
  636     {
  637         plot.grid = gtk_databox_grid_new (9, 3, &color, 1);
  638         for (i = 0; i < 11; i++)
  639         {
  640             if (pltype == 3)
  641                 g_sprintf (tmp_str, "%u", 100 - 10*i);
  642             else
  643                 g_sprintf (tmp_str, "%u", 10 - i);
  644             gtk_label_set_text (GTK_LABEL (plot.label_y[i]), tmp_str);
  645             gtk_label_set_xalign (GTK_LABEL (plot.label_y[i]), 0.9);
  646             gtk_label_set_yalign (GTK_LABEL (plot.label_y[i]), i / 10.0);
  647             gtk_widget_show (plot.label_y[i]);
  648         }
  649         for (; i < MAX_Y_LABELS; i++)
  650             gtk_widget_hide (plot.label_y[i]);
  651     }
  652     gtk_databox_graph_add (GTK_DATABOX (plot.databox), plot.grid);
  653 
  654     /* Redraw the plot
  655      */
  656     gtk_widget_show_all (plot.gtkgrid);
  657 }
  658 
  659 void
  660 plot_pointer_update (gdouble x)
  661 {
  662     static glong n_prev = 0;
  663     glong n = 0;
  664     gchar *xstr;
  665     gchar *ystr;
  666     gchar *tmp;
  667     GtkDatabox *box;
  668     gint width;
  669 
  670     box = GTK_DATABOX (plot.databox);
  671 
  672     gtk_window_get_size (get_win ("window_stat"), &width, NULL);
  673     if (plot_type > 4)
  674         width -= 25;
  675     else
  676         width -= 50;
  677 
  678     n = rintf (x / width * (DATA_POINTS + 2)) - 1; // Round integer from float
  679     if (n == n_prev)
  680         return;
  681     n_prev = n;
  682 
  683     if (n < 0 || n >= n_points || n >= DATA_POINTS) 
  684     {
  685         xstr = g_strdup ("--");
  686         ystr = g_strdup ("--");
  687         plot.mark.x[0] = -7;
  688         plot.mark.y[0] = -7;
  689     }
  690     else
  691     {
  692         if (plot_type < 6)
  693         {
  694             if (tutor_get_type () == TT_BASIC && lesson_n == 0)
  695                 xstr = g_strdup_printf ("%s - %s [%s]", date[n], hour[n], lesson[n]);
  696             else
  697                 xstr = g_strdup_printf ("%s - %s", date[n], hour[n]);
  698             ystr = g_strdup_printf ("%.2f", plot.data.y[n]);
  699         }
  700         else if (plot_type == 6)
  701         {
  702             xstr = accur_terror_char_utf8 (n);
  703             ystr = g_strdup_printf ("%.0f/%.lu", plot.data.y[n], accur_error_total ());
  704         }
  705         else
  706         {
  707             xstr = accur_ttime_char_utf8 (n);
  708             ystr = g_strdup_printf ("%.3f", plot.data.y[n]);
  709         }
  710         plot.mark.x[0] = plot.data.x[n];
  711         plot.mark.y[0] = plot.data.y[n];
  712     }
  713     gtk_entry_set_text (GTK_ENTRY (get_wg ("entry_stat_x")), xstr);
  714     gtk_entry_set_text (GTK_ENTRY (get_wg ("entry_stat_y")), ystr);
  715     gtk_widget_queue_draw (plot.databox);
  716     g_free (xstr);
  717     g_free (ystr);
  718 }