/* trace.c */ #include #include #include #include #include "globaldefs.h" #include "keyboard.h" #include "smileys.h" #include "text.h" #include "trace.h" /* the ratio to "mix" older speed and new ones higher means don't change so much */ #define Mixratio 0.6 /* the "standard" speed */ #define StandardSpeed 500 /* numbers of charcters per word */ #define CharPerWord 5 extern list *speedsmileys, *mistakesmileys ; #ifdef _NO_PROTO static void sethighlight() ; #else static void sethighlight(Widget, XmTextPosition) ; #endif char inputc = '\0' ; int lastrow = ERROR, lastcol, nextrow = ERROR, nextcol ; void UpdateKeyboard(scrolledtext, pos) Widget scrolledtext ; XmTextPosition pos ; /* position of next key to show */ { /* lastrow, lastcol is the key pushed in and nextrow, nextcol is the key reversevideoed. */ int row, col, shift ; char c = ' ' ; XmString label ; row = rowsp[inputc] ; col = colsp[inputc] ; /* retract pushed in key */ if (row != ERROR) { if (inputc == p_tab[0][row][col]) { c = e_tab[0][row][col] ; shift = -1 ; } else { c = e_tab[1][row][col] ; if (col < lr_tab[row]) shift = 1 ; else shift = 0 ; } if (lastrow != ERROR) { XtVaSetValues(keyframes[lastrow][lastcol], XmNshadowType, XmSHADOW_OUT, NULL) ; if (isspace(e_tab[0][lastrow][lastcol])) sprintf(buf, Spacebar) ; else sprintf(buf, " %c ", app_data.show_keytop ? e_tab[0][lastrow][lastcol] : ' ') ; label = XmStringCreateSimple(buf) ; XtVaSetValues(keys[lastrow][lastcol], XmNlabelString, label, NULL) ; XmStringFree(label) ; } if (shift != lastshift) { if (lastshift != -1) XtVaSetValues(shiftframes[lastshift], XmNshadowType, XmSHADOW_OUT, NULL) ; if (shift != -1) XtVaSetValues(shiftframes[shift], XmNshadowType, XmSHADOW_IN, NULL) ; lastshift = shift ; } } /* reset reverse-videoed-key */ if (nextrow != ERROR) { if (isspace(e_tab[0][nextrow][nextcol])) sprintf(buf, Spacebar) ; else sprintf(buf, " %c ", app_data.show_keytop ? e_tab[0][nextrow][nextcol] : ' ') ; label = XmStringCreateSimple(buf) ; if (IsHomePosition(nextrow, nextcol)) XtVaSetValues(keys[nextrow][nextcol], XmNlabelString, label, XmNbackgroundPixmap, keytoppm25, XmNforeground, fgpix, NULL) ; else XtVaSetValues(keys[nextrow][nextcol], XmNforeground, fgpix, XmNlabelString, label, XmNbackground, bgpix, NULL) ; XmStringFree(label) ; } /* push key */ if (row != ERROR) { XtVaSetValues(keyframes[row][col], XmNshadowType, XmSHADOW_IN, NULL) ; if (isspace(c)) sprintf(buf, Spacebar) ; else sprintf(buf, " %c ", c) ; label = XmStringCreateSimple(buf) ; XtVaSetValues(keys[row][col], XmNlabelString, label, NULL) ; XmStringFree(label) ; lastrow = row ; lastcol = col ; } if (app_data.show_next_key) { c = TextGetChar(scrolledtext, pos) ; nextrow = row = rowse[c] ; nextcol = col = colse[c] ; } else { nextrow = ERROR ; nextcol = ERROR ; } /* reverse-video key */ if (row != ERROR && app_data.show_next_key) { shift = -1 ; if (isspace(c)) c = ' ' ; if ((!app_data.ignore_cases || !isalpha(c)) && c != e_tab[0][row][col]) if (col < lr_tab[row]) shift = 1 ; else shift = 0 ; if (isspace(c)) sprintf(buf, Spacebar) ; else sprintf(buf, " %c ", c) ; label = XmStringCreateSimple(buf) ; if (IsHomePosition(row, col)) XtVaSetValues(keys[row][col], XmNlabelString, label, XmNbackgroundPixmap, keytoppmbg, XmNforeground, bgpix, NULL) ; else XtVaSetValues(keys[row][col], XmNlabelString, label, XmNforeground, bgpix, XmNbackground, fgpix, NULL) ; XmStringFree(label) ; if (shift != nextshift) { if (nextshift != -1) XtVaSetValues(shifts[nextshift], XmNforeground, fgpix, XmNbackground, bgpix, NULL) ; if (shift != -1) XtVaSetValues(shifts[shift], XmNforeground, bgpix, XmNbackground, fgpix, NULL) ; nextshift = shift ; } } } int lastwasspace, punctbeforespace ; int blink ; int lastnumchars, numchars, speed ; int bol, eol ; int mistakes, lastwasmistake ; int mistakehappy, speedhappy ; double mistakeratio ; time_t lasttime ; void TextModify(w, scrolledtext, cbs) Widget w ; Widget scrolledtext ; XmTextVerifyCallbackStruct *cbs ; { XEvent *event = cbs->event ; int row, col, pos, i ; int currspeed ; time_t now = time(NULL) ; TextUserData *textu = GetUserData(scrolledtext) ; char c ; double d ; if (!lasttime) lasttime = now ; pos = cbs->currInsert ; if (event) { switch (event->xany.type) { case KeyPress: cbs->doit = False ; /* check if text is empty */ if (XmTextGetLastPosition(scrolledtext) == 0) return ; if (cbs->text->length != 1) { /* probably backspace */ /* printf("???? length = %d, char = %c\n", cbs->text->length, cbs->text->ptr[1]) ; */ break ; } for (i = 0 ; i < 1 ; i++) { /* move cursor if hit correct key */ row = rowsp[cbs->text->ptr[i]] ; col = colsp[cbs->text->ptr[i]] ; if (cbs->text->ptr[i] == p_tab[0][row][col]) c = e_tab[0][row][col] ; else c = e_tab[1][row][col] ; inputc = isspace(cbs->text->ptr[i]) ? ' ' : cbs->text->ptr[i] ; if ((app_data.ignore_cases ? (tolower(TextGetChar(scrolledtext, pos)) == tolower(c)) : (TextGetChar(scrolledtext, pos) == c)) || (isspace(TextGetChar(scrolledtext, pos)) && isspace(c))) { /* correct key */ pos++ ; numchars++ ; if (blink) blink = False ; lastwasmistake = lastwasspace = False ; if (isspace(c)) lastwasspace = True ; else /* changed temporarily to disable special treatment for spaces after punctuations */ punctbeforespace = False ; /* punctbeforespace = ispunct(c) ; */ } else { /* incorrect key */ if (isspace(c)) { /* jump to next non-space */ while (!isspace(TextGetChar(scrolledtext, pos))) pos++ ; while (isspace(TextGetChar(scrolledtext, pos))) pos++ ; } blink = !blink ; if (!lastwasmistake) { mistakes++ ; lastwasmistake = True ; } } /* skip multiple spaces and keys that can't be hit but don't skip multiple blanks after punctuations */ while (rowsp[TextGetChar(scrolledtext, pos)] == ERROR || (isspace(TextGetChar(scrolledtext, pos)) && lastwasspace && !(punctbeforespace && TextGetChar(scrolledtext, pos) == ' '))) { if (TextGetChar(scrolledtext, pos) == '\n') punctbeforespace = False ; pos++ ; } /* wraparound at the end of file */ if (pos >= XmTextGetLastPosition(scrolledtext)) pos = 0 ; } XmTextSetInsertionPosition(scrolledtext, pos) ; /* so that keyboard is updated even if cursor didn't move */ if (cbs->currInsert == pos) { sethighlight(scrolledtext, pos) ; UpdateKeyboard(scrolledtext, pos) ; } /* show three lines above cursor */ for (i = pos ; i > 0 ; i--) if (TextGetChar(scrolledtext, i) == '\n') break ; if (i > 0) i-- ; for ( ; i > 0 ; i--) if (TextGetChar(scrolledtext, i) == '\n') break ; if (i > 0) i-- ; for ( ; i > 0 ; i--) if (TextGetChar(scrolledtext, i) == '\n') break ; XmTextSetTopCharacter(scrolledtext, i) ; ShowNext(scrolledtext, pos, 10) ; break ; default: if (!textu->isreading) cbs->doit = False ; break ; } if (now - lasttime > 5) { currspeed = (numchars-lastnumchars)*60/(now-lasttime) ; i = speed * Mixratio + currspeed*(1-Mixratio) ; if (i > speed) { if (speedhappy < Maxsmileys) { AdjustSmileys(speedhappy, speedhappy+1, speedsmileys) ; speedhappy++ ; } } else if (i < speed) if (speedhappy > -Maxsmileys+1) { AdjustSmileys(speedhappy, speedhappy-1, speedsmileys) ; speedhappy-- ; } speed = i ; if (numchars == lastnumchars || currspeed == 0) d = mistakeratio ; else if (mistakes == 0) d = mistakeratio = mistakeratio * StandardSpeed / (currspeed + StandardSpeed) ; else if (mistakeratio != 0) d = mistakeratio = exp(log(mistakeratio) * StandardSpeed / (currspeed + StandardSpeed) + log((double) mistakes/(numchars-lastnumchars)) * currspeed / (currspeed + StandardSpeed)) ; else d = mistakeratio = exp(log(0.1) * StandardSpeed / (currspeed + StandardSpeed) + log((double) mistakes/(numchars-lastnumchars)) * currspeed / (currspeed + StandardSpeed)) ; for (i = -Maxsmileys+1 ; d < 0.1 && i < Maxsmileys ; d *= 1.5, i++) ; AdjustSmileys(mistakehappy, i, mistakesmileys) ; mistakehappy = i; mistakes = 0 ; RedrawStats(scrolledtext) ; lastnumchars = numchars ; lasttime = now ; } } else if (!textu->isreading) cbs->doit = False ; } static void sethighlight(scrolledtext, pos) Widget scrolledtext ; XmTextPosition pos ; /* highlight cursor and one line around it if necessary */ { int last = XmTextGetLastPosition(scrolledtext) ; XmTextSetHighlight(scrolledtext, 0, last, XmHIGHLIGHT_NORMAL) ; for (bol = pos ; bol >= 0 ; bol--) if (TextGetChar(scrolledtext, bol) == '\n') { bol++ ; break ; } for (eol = pos ; eol <= last ; eol++) if (TextGetChar(scrolledtext, eol) == '\n') break ; if (eol < last) eol++ ; XmTextSetHighlight(scrolledtext, bol, eol, blink ? XmHIGHLIGHT_SELECTED : XmHIGHLIGHT_NORMAL) ; XmTextSetHighlight(scrolledtext, pos, pos+1, blink ? XmHIGHLIGHT_SECONDARY_SELECTED : XmHIGHLIGHT_SELECTED) ; } void CursorMove(w, scrolledtext, cbs) Widget w, scrolledtext ; XmTextVerifyCallbackStruct *cbs ; { sethighlight(scrolledtext, cbs->newInsert) ; UpdateKeyboard(scrolledtext, cbs->newInsert) ; } void ReinitStats(scrolledtext) Widget scrolledtext ; { lastwasspace = False ; punctbeforespace = False ; lastwasmistake = False ; blink = False ; lasttime = (time_t) 0 ; lastnumchars = 0 ; numchars = 0 ; speed = 0 ; mistakes = 0 ; AdjustSmileys(speedhappy, 1, speedsmileys) ; AdjustSmileys(mistakehappy, 1, mistakesmileys) ; mistakehappy = speedhappy = 1 ; mistakeratio = 0.0 ; XtVaSetValues(scrolledtext, XmNforeground, fgpix, XmNbackground, bgpix, NULL) ; RedrawStats(scrolledtext) ; } void RedrawStats(scrolledtext) Widget scrolledtext ; { XmString label; if (app_data.word_display) sprintf(buf, "Speed: %3d wpm", speed/CharPerWord) ; else sprintf(buf, "Speed: %3d cpm", speed) ; label = XmStringCreateSimple(buf) ; XtVaSetValues(speedbox, XmNlabelString, label, NULL) ; XmStringFree(label) ; if (app_data.word_display) sprintf(buf, "Mistakes: %.3f mpw", (double)mistakeratio*CharPerWord) ; else sprintf(buf, "Mistakes: %.3f mpc", (double)mistakeratio) ; label = XmStringCreateSimple(buf) ; XtVaSetValues(mistakebox, XmNlabelString, label, NULL) ; XmStringFree(label) ; }