@@ 8,23 8,28 @@
#include "result.h"
#include "timer.h"
#include <curses.h>
+#include <stdio.h>
#include <stdlib.h>
#include <grapheme.h>
-/* checks if pointer is a valid wide character */
-static inline int is_wc(char **);
-/* returns the attributes to draw the current characters with */
-static inline int get_attr(char **, int);
-static inline char *get_wc(char **);
-static size_t word_length(list_node_t **);
+static double twin_timer_diff(Timer **);
+static int twin_completed(TWindow *);
static size_t twin_remove_line(TWindow *);
+static void twin_add_key(TWindow *, char);
+static void twin_backspace(TWindow *);
+static void twin_clear(TWindow *);
static void twin_draw_sentences(TWindow *);
-static bool twin_completed(TWindow *);
-static inline void twin_title_time(TWindow *);
-static inline void twin_title_words(TWindow *);
+static void twin_space(TWindow *);
static void twin_title(TWindow *);
-static double twin_timer_diff(Timer **);
-static bool word_incorrect(list_node_t **);
+static void twin_title_time(TWindow *);
+static void twin_title_words(TWindow *);
+static void twin_update_position(TWindow *);
+
+static int _get_attr(char **, int);
+static int _get_wc(char **);
+static int _is_wc(char **);
+static int _word_incorrect(list_node_t **);
+static size_t _word_bytes(list_node_t **);
TWindow *
twin_new(Result *result)
@@ 48,15 53,6 @@ twin_new(Result *result)
return twin;
}
-static void
-twin_clear(TWindow *twin)
-{
- list_destroy(twin->sentences[0]);
- list_destroy(twin->sentences[1]);
- free(twin->timers[0]);
- free(twin->timers[1]);
-}
-
void
twin_free(TWindow *twin)
{
@@ 110,95 106,6 @@ twin_update(TWindow *twin)
return 0;
}
-static void
-twin_update_position(TWindow *twin)
-{
- twin->position = (Point) {
- 0, 0
- };
- list_iterator_t *it[2] = {
- list_iterator_new(twin->sentences[0], LIST_HEAD),
- list_iterator_new(twin->sentences[1], LIST_HEAD),
- };
- list_node_t *word[2] = {
- list_iterator_next(it[0]),
- list_iterator_next(it[1]),
- };
- while (word[0]) {
- size_t len = word_length(word);
- /* move onto next line if word is too long */
- if (twin->position.x + len >= WIDTH) {
- twin->position.x = 0;
- ++twin->position.y;
- }
- /* if there are more words, account for space between them */
- if (word[0]->next)
- twin->position.x += len + 1;
- /* otherwise, move the cursor by input characters */
- else
- twin->position.x += strlen(twin->sentences[0]->tail->val);
- word[0] = list_iterator_next(it[0]);
- word[1] = list_iterator_next(it[1]);
- }
- list_iterator_destroy(it[0]);
- list_iterator_destroy(it[1]);
- mvprintw(2, 0, "%u %u\n", twin->position.x, twin->position.y);
-}
-
-static void
-twin_space(TWindow *twin)
-{
- sentence_add_word(twin->sentences[0], "");
- /* TODO: remove chars when the word is typed incorrectly */
- ++twin->result->chars[0];
- twin_update_position(twin);
- if (twin->position.y >= 2) {
- twin_remove_line(twin);
- twin_update_position(twin);
- }
- /* TODO: remove line instead of going to 3rd line */
-}
-
-static void
-twin_add_key(TWindow *twin, char key)
-{
- /* prevent wrapping word onto next line */
- if (twin->position.x >= WIDTH - 1)
- return;
- if (sentence_add_key(twin->sentences[0], key))
- return;
- ++twin->result->chars[0];
- /* update cursor position */
- /* wrap onto next line if at the end of line */
- if (twin->position.x >= WIDTH - 1) {
- ++twin->position.y;
- list_node_t *words[2] = {twin->sentences[0]->tail, twin->sentences[1]->tail};
- twin->position.x = word_length(words);
- return;
- }
- /* otherwise move right */
- ++twin->position.x;
-}
-
-static inline void
-twin_backspace(TWindow *twin)
-{
- switch (sentence_remove_key(twin->sentences[0])) {
- case 1: /* skip if nothing to do */
- return;
- case 0:
- --twin->position.x;
- break;
- case 2:
- if (twin->position.x == 0 && twin->position.y == 0)
- return;
- twin_update_position(twin);
- break;
- }
- --twin->result->chars[0];
- wclear(twin->panel->window);
-}
-
int
twin_input(TWindow *twin)
{
@@ 236,45 143,31 @@ twin_input(TWindow *twin)
return -1;
}
-static inline int
-is_wc(char **chars)
-{
- return ((chars[0] && *chars[0]) || (chars[1] && *chars[1]));
-}
-
-static inline int
-get_attr(char **chars, int is_last)
-{
- /* TODO: figure out optimal order and conditions of the if statements */
- /* bg if haven't reached this point */
- if (!chars[0])
- return COLOR_PAIR(TPAIR_BG);
- if (!*chars[0] && is_last)
- return COLOR_PAIR(TPAIR_BG);
- /* fg if characters match */
- if (chars[1] && *chars[0] == *chars[1])
- return COLOR_PAIR(TPAIR_FG);
- /* err otherwise */
- if (!*chars[0])
- return COLOR_PAIR(TPAIR_ERR);
- return COLOR_PAIR(TPAIR_ERR) | A_BOLD;
-}
-
-static inline char *
-get_wc(char **chars)
+static double
+twin_timer_diff(Timer **timers)
{
- if (!chars[1] || !*chars[1])
- return chars[0];
- return chars[1];
+ if (!timers[1])
+ timers[1] = malloc(sizeof(Timer));
+ timer_now(timers[1]);
+ return timer_diff(timers[0], timers[1]);
}
-static size_t
-word_length(list_node_t **word)
+static int
+twin_completed(TWindow *twin)
{
- size_t a = word[0] ? grapheme_next_word_break_utf8(word[0]->val, SIZE_MAX) : 0,
- b = word[1] ? grapheme_next_word_break_utf8(word[1]->val, SIZE_MAX) : 0;
- mvprintw(5, 0, "length %lu %lu\n", a, b);
- return a > b ? a : b;
+ /* TODO: support completig on last character */
+ switch (twin->result->mode) {
+ case MODE_TIME:
+ if (!twin->timers[1])
+ return false;
+ return timer_diff(twin->timers[0], twin->timers[1]) >= twin->result->length;
+ case MODE_WORDS:
+ case MODE_QUOTE:
+ return twin->sentences[0]->len > twin->sentences[1]->len;
+ case MODE_ZEN:
+ return false;
+ }
+ return false;
}
static size_t
@@ 285,7 178,7 @@ twin_remove_line(TWindow *twin)
do {
word[0] = list_lpop(twin->sentences[0]);
word[1] = list_lpop(twin->sentences[1]);
- line_length += word_length(word);
+ line_length += _word_bytes(word);
if (word[0]) {
free(word[0]->val);
free(word[0]);
@@ 294,7 187,7 @@ twin_remove_line(TWindow *twin)
free(word[1]->val);
free(word[1]);
}
- next_word_length = word_length((list_node_t *[2]) {
+ next_word_length = _word_bytes((list_node_t *[2]) {
twin->sentences[0]->head, twin->sentences[1]->head
});
if (line_length + next_word_length >= WIDTH - 1)
@@ 306,13 199,63 @@ twin_remove_line(TWindow *twin)
}
static void
+twin_add_key(TWindow *twin, char key)
+{
+ /* prevent wrapping word onto next line */
+ if (twin->position.x >= WIDTH - 1)
+ return;
+ if (sentence_add_key(twin->sentences[0], key))
+ return;
+ ++twin->result->chars[0];
+ /* update cursor position */
+ list_node_t *words[2] = {twin->sentences[0]->tail, twin->sentences[1]->tail};
+ /* wrap onto next line if at the end of line */
+ if (twin->position.x >= WIDTH - 1) {
+ ++twin->position.y;
+ twin->position.x = _word_bytes(words);
+ return;
+ }
+ /* otherwise move right */
+ twin->position.x += 2;
+}
+
+static void
+twin_backspace(TWindow *twin)
+{
+ switch (sentence_remove_key(twin->sentences[0])) {
+ case 1: /* skip if nothing to do */
+ return;
+ case 0:
+ --twin->position.x;
+ break;
+ case 2:
+ if (twin->position.x == 0 && twin->position.y == 0)
+ return;
+ twin_update_position(twin);
+ break;
+ }
+ --twin->result->chars[0];
+ wclear(twin->panel->window);
+}
+
+static void
+twin_clear(TWindow *twin)
+{
+ list_destroy(twin->sentences[0]);
+ list_destroy(twin->sentences[1]);
+ free(twin->timers[0]);
+ free(twin->timers[1]);
+}
+
+static void
twin_draw_sentences(TWindow *twin)
{
/* TODO: draw wrongly typed letter underneath mistake */
/* TODO: handle newline characters */
WINDOW *win = twin->panel->window;
- char *chars[2] = {NULL, NULL}, *c = NULL;
- int attr;
+ char *chars[2] = {NULL, NULL};
+ int attr, c, last;
+ size_t ret[2];
Point position = {0, TWIN_STATUS_HEIGHT};
wmove(win, position.y, position.x);
list_iterator_t *it[2] = {
@@ 328,29 271,29 @@ twin_draw_sentences(TWindow *twin)
/* draw word */
chars[0] = word[0] ? word[0]->val : NULL;
chars[1] = word[1] ? word[1]->val : NULL;
- while (is_wc(chars)) {
+ while (_is_wc(chars)) {
/* add the character */
- c = get_wc(chars);
- bool last = (word[0]) ? (word[0]->next == NULL) : false;
- attr = get_attr(chars, last);
- if (word_incorrect(word))
+ c = _get_wc(chars);
+ ret[0] = grapheme_next_character_break_utf8(chars[0], SIZE_MAX);
+ ret[1] = grapheme_next_character_break_utf8(chars[1], SIZE_MAX);
+ last = (word[0]) ? (word[0]->next == NULL) : 0;
+ attr = _get_attr(chars, last);
+ if (_word_incorrect(word))
attr |= A_UNDERLINE;
wattron(win, attr);
- waddnstr(win, c, 1);
+ waddnstr(win, chars[c], ret[c]);
wattroff(win, attr);
/* advance characters */
- if (chars[0] && *chars[0])
- ++chars[0];
- if (chars[1] && *chars[1])
- ++chars[1];
+ chars[0] += ret[0];
+ chars[1] += ret[1];
}
- position.x += word_length(word);
+ position.x += _word_bytes(word);
/* advance to next word */
word[0] = list_iterator_next(it[0]);
word[1] = list_iterator_next(it[1]);
/* add seperator */
if (word[0] || word[1]) {
- if (position.x + word_length(word) >= WIDTH - 1) {
+ if (position.x + _word_bytes(word) >= WIDTH - 1) {
/* don't draw past last line */
if (position.y >= HEIGHT + TWIN_STATUS_HEIGHT)
break;
@@ 369,21 312,33 @@ twin_draw_sentences(TWindow *twin)
list_iterator_destroy(it[1]);
}
-static bool
-twin_completed(TWindow *twin)
+static void
+twin_space(TWindow *twin)
+{
+ sentence_add_word(twin->sentences[0], "");
+ /* TODO: remove chars when the word is typed incorrectly */
+ ++twin->result->chars[0];
+ twin_update_position(twin);
+ if (twin->position.y >= 2) {
+ twin_remove_line(twin);
+ twin_update_position(twin);
+ }
+ /* TODO: remove line instead of going to 3rd line */
+}
+
+static void
+twin_title(TWindow *twin)
{
switch (twin->result->mode) {
case MODE_TIME:
- if (!twin->timers[1])
- return false;
- return timer_diff(twin->timers[0], twin->timers[1]) >= twin->result->length;
- case MODE_WORDS:
+ twin_title_time(twin);
+ break;
case MODE_QUOTE:
- return twin->sentences[0]->len > twin->sentences[1]->len;
+ case MODE_WORDS:
case MODE_ZEN:
- return false;
+ twin_title_words(twin);
+ break;
}
- return false;
}
static inline void
@@ 415,39 370,84 @@ twin_title_words(TWindow *twin)
sprintf(twin->panel->title, "%zu", len[0]);
}
+/* Recalculate cursor position */
static void
-twin_title(TWindow *twin)
+twin_update_position(TWindow *twin)
{
- switch (twin->result->mode) {
- case MODE_TIME:
- twin_title_time(twin);
- break;
- case MODE_QUOTE:
- case MODE_WORDS:
- case MODE_ZEN:
- twin_title_words(twin);
- break;
+ twin->position = (Point) {
+ 0, 0
+ };
+ list_iterator_t *it[2] = {
+ list_iterator_new(twin->sentences[0], LIST_HEAD),
+ list_iterator_new(twin->sentences[1], LIST_HEAD),
+ };
+ list_node_t *word[2] = {
+ list_iterator_next(it[0]),
+ list_iterator_next(it[1]),
+ };
+ size_t len;
+ while (word[0]) {
+ len = _word_bytes(word);
+ /* move onto next line if word is too long */
+ if (twin->position.x + len >= WIDTH) {
+ twin->position.x = 0;
+ ++twin->position.y;
+ }
+ /* if there are more words, account for space between them */
+ if (word[0]->next)
+ twin->position.x += len + 1;
+ /* otherwise, move the cursor by input characters */
+ //else
+ // twin->position.x += grapheme_next_word_break_utf8(twin->sentences[0]->tail->val, SIZE_MAX);
+ word[0] = list_iterator_next(it[0]);
+ word[1] = list_iterator_next(it[1]);
}
+ list_iterator_destroy(it[0]);
+ list_iterator_destroy(it[1]);
+ mvprintw(2, 0, "%u %u\n", twin->position.x, twin->position.y);
}
-static double
-twin_timer_diff(Timer **timers)
+static inline int
+_get_attr(char **chars, int is_last)
{
- if (!timers[1])
- timers[1] = malloc(sizeof(Timer));
- timer_now(timers[1]);
- return timer_diff(timers[0], timers[1]);
+ /* TODO: figure out optimal order and conditions of the if statements */
+ /* bg if haven't reached this point */
+ if (!chars[0])
+ return COLOR_PAIR(TPAIR_BG);
+ if (!*chars[0] && is_last)
+ return COLOR_PAIR(TPAIR_BG);
+ /* fg if characters match */
+ if (chars[1] && *chars[0] == *chars[1])
+ return COLOR_PAIR(TPAIR_FG);
+ /* err otherwise */
+ if (!*chars[0])
+ return COLOR_PAIR(TPAIR_ERR);
+ return COLOR_PAIR(TPAIR_ERR) | A_BOLD;
}
-static bool
-word_incorrect(list_node_t **words)
+static inline int
+_get_wc(char **chars)
+{
+ if (!chars[1] || !*chars[1])
+ return 0;
+ return 1;
+}
+
+static inline int
+_is_wc(char **chars)
+{
+ return ((chars[0] && *chars[0]) || (chars[1] && *chars[1]));
+}
+
+static int
+_word_incorrect(list_node_t **words)
{
if (!words[0] || !words[1])
return false;
if (!strcmp(words[0]->val, words[1]->val))
return false;
char *wc[2] = {words[0]->val, words[1]->val};
- while (is_wc(wc)) {
+ while (_is_wc(wc)) {
if (wc[0] && *wc[0] && wc[1] && *wc[1] && *wc[0] != *wc[1])
return true;
if (wc[0] && *wc[0])
@@ 457,3 457,22 @@ word_incorrect(list_node_t **words)
}
return false;
}
+
+/* Return the length of the longer word in bytes */
+static size_t
+_word_bytes(list_node_t **word)
+{
+ size_t length = 0, ret[2];
+ char *chars[] = {
+ word[0] ? word[0]->val : NULL,
+ word[1] ? word[1]->val : NULL,
+ };
+ while (_is_wc(chars)) {
+ ret[0] = grapheme_next_character_break_utf8(chars[0], SIZE_MAX);
+ ret[1] = grapheme_next_character_break_utf8(chars[1], SIZE_MAX);
+ length += ret[0] > ret[1] ? ret[0] : ret[1];
+ chars[0] += ret[0];
+ chars[1] += ret[1];
+ }
+ return length;
+}