Logo Search packages:      
Sourcecode: latrine version File versions  Download package

screen.c

/* vim: set noet ts=4:
 *
 * Copyright (c) 2002-2007 Martin A. Godisch <martin@godisch.de>.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
 * St, Fifth Floor, Boston, MA 02110-1301, USA.
 */
#include <charset.h>
#include <data.h>
#include <latrine.h>
#include <memory.h>
#include <ncursesw/ncurses.h>
#include <options.h>
#include <screen.h>
#include <unistd.h>
#include <wait.h>

#define LEFT        10
#define RIGHT       10
#define STARTINPUT  (LEFT + max_lang + 3)
#define MAXINPUT    (sx - RIGHT - LEFT - max_lang - 4)
#define MEVENTSIZE  64

static int
      max_lang      = 0,
      max_mode      = 0,
      reverse       = 0,
      phrase_offset = 0;
static struct word
      *p = NULL;
static char line1[BUFSIZE] = "";
static char line2[BUFSIZE] = "";
int sx = 0,
      sy = 0;

static inline void update_mode(int change)
{
      int i, x, y;

      if (max_mode == 0) {
            max_mode = strlen(L10N_NORMAL);
            if ((i = strlen(L10N_REVERSE)) > max_mode)
                  max_mode = i;
            if ((i = strlen(L10N_MIXED))   > max_mode)
                  max_mode = i;
      }
      bkgdset(COLOR_PAIR(COLOR_LINE));
      mvaddstr(0, sx - max_mode - 8, "[");
      if (change)
            switch (mode) {
            case MODE_NORMAL:
                  mode = MODE_REVERSE;
                  break;
            case MODE_REVERSE:
                  mode = MODE_MIXED;
                  break;
            case MODE_MIXED:
                  mode = MODE_NORMAL;
                  break;
            }
      switch (mode) {
      case MODE_NORMAL:
            addstr(L10N_NORMAL);
            break;
      case MODE_REVERSE:
            addstr(L10N_REVERSE);
            break;
      case MODE_MIXED:
            addstr(L10N_MIXED);
            break;
      }
      getyx(stdscr, y, x);
      for (i = x; i < sx - 7; i++)
            addch(' ');
      addch(']');
}

static inline void update_dir(int change)
{
      if (change)
            direction = (direction + 1) % 4;
      bkgdset(COLOR_PAIR(COLOR_LINE));
      move(0, sx - 5);
      switch (direction) {
      case 0:
            addstr("[>>]");
            break;
      case 1:
            addstr("[<>]");
            break;
      case 2:
            addstr("[><]");
            break;
      case 3:
            addstr("[<<]");
            break;
      default:
            assert(0);
      }
}

static inline void update_counter(int change)
{
      static size_t
            correct  = 0,
            mistaken = 0;
      char s[40];

      switch (change) {
      case +1:
            correct++;
            break;
      case -1:
            mistaken++;
      }
      sprintf(s, "%d/%d (%d %%)", correct, correct + mistaken, (correct + mistaken > 0) ? (int)((double)(correct * 100) / (double)(correct + mistaken) + 0.5) : 0);
      bkgdset(COLOR_PAIR(COLOR_BACK));
      move(6, 0);
      clrtoeol();
      mvaddstr(6, STARTINPUT + MAXINPUT + 1 - strlen(s), s);
}

static inline void makestatuslines(void)
{
      bkgdset(COLOR_PAIR(COLOR_LINE));
      move(0, 0);
      clrtoeol();
      addch(' ');
      addstr(PACKAGE_STRING);
      move(sy - 1, 0);
      clrtoeol();
      attron(A_BOLD);
      addstr(" ESC ");
      attroff(A_BOLD);
      addstr(_("Exit"));
      attron(A_BOLD);
      addstr("  F1 ");
      attroff(A_BOLD);
      addstr(_("Help"));
      attron(A_BOLD);
      addstr("  F2 ");
      attroff(A_BOLD);
      addstr(_("Reload"));
      attron(A_BOLD);
      addstr("  F3 ");
      attroff(A_BOLD);
      addstr(_("Mode"));
      attron(A_BOLD);
      addstr("  F4 ");
      attroff(A_BOLD);
      addstr(_("Direction"));
      attron(A_BOLD);
      addstr("  TAB ");
      attroff(A_BOLD);
      addstr(_("Next"));
      update_mode(0);
      update_dir(0);
}

static inline void update_phrase(void)
{
      wchar_t buffer[BUFSIZE];
      char    local[BUFSIZE];
      int r2l = (reverse && direction / 2) || (!reverse && direction % 2);
      int  i, n, x, y;
      struct Tlang *l;

      local[sizeof(local)-1] = 0;
      strncpy(local, p->lang[reverse ? 1 : 0]->c, sizeof(local) - 1);
      for (l = p->lang[reverse ? 1 : 0]->next; l != NULL; l = l->next) {
            strncat(local + strlen(local), ", ", sizeof(local) - strlen(local) - 1);
            strncat(local + strlen(local), l->c, sizeof(local) - strlen(local) - 1);
      }
      bkgdset(COLOR_PAIR(COLOR_INPUT));
      move(y = reverse ? 12 : 10, STARTINPUT);
      mbstowcs(buffer, local, BUFSIZE);
      n = wcstrlen(buffer);
      if (r2l) {
            if (phrase_offset > 0)
                  phrase_offset = 0;
            if (phrase_offset < (i = MAXINPUT - n))
                  phrase_offset = i < 0 ? i : 0;
            if (n > MAXINPUT + phrase_offset)
                  memmove(buffer, buffer + n - MAXINPUT + phrase_offset, MAXINPUT * sizeof(wchar_t));
      } else {
            if (phrase_offset < 0)
                  phrase_offset = 0;
            if (phrase_offset > (i = n - MAXINPUT))
                  phrase_offset = i > 0 ? i : 0;
            if (phrase_offset > 0)
                  memmove(buffer, buffer + phrase_offset, MAXINPUT * sizeof(wchar_t));
      }
      buffer[MAXINPUT] = 0L;
      wcstombs(local, buffer, BUFSIZE);
      if (r2l) {
            for (i = 0; i < MAXINPUT - n; i++)
                  addch(' ');
            x = i;
            addstr(local);
      } else {
            addstr(local);
            for (x = i = n; i < MAXINPUT; i++)
                  addch(' ');
      }
      move(y, STARTINPUT + x);
      refresh();
}

static inline void update_translation(const wchar_t *answer)
{
      int r2l = (reverse && direction % 2) || (!reverse && direction / 2);
      wchar_t buffer[BUFSIZE];
      char     local[BUFSIZE];
      size_t n;
      int i, y;

      bkgdset(COLOR_PAIR(COLOR_INPUT));
      move(y = reverse ? 10 : 12, STARTINPUT);
      if (answer == NULL) {
            for (i = 0; i < MAXINPUT; i++)
                  addch(' ');
            if (r2l)
                  move(y, STARTINPUT + MAXINPUT - 1);
            else
                  move(y, STARTINPUT);
            return;
      }
      if ((n = wcstrlen(answer)) < MAXINPUT)
            memcpy(buffer, answer, (n + 1) * sizeof(wchar_t));
      else {
            n = n < BUFSIZE - 1 ? MAXINPUT - 1 : MAXINPUT;
            memcpy(buffer, answer + wcstrlen(answer) - n, (n + 1) * sizeof(wchar_t));
      }
      if (r2l) {
            wcsreverse(buffer);
            wcstombs(local, buffer, sizeof(local));
            for (i = 0; i < MAXINPUT - n; i++)
                  addch(' ');
            addstr(local);
            move(y, STARTINPUT + MAXINPUT - n - (n == MAXINPUT ? 0 : 1));
      } else {
            wcstombs(local, buffer, sizeof(local));
            addstr(local);
            for (i = n; i < MAXINPUT; i++)
                  addch(' ');
            move(y, STARTINPUT + n - (n == MAXINPUT ? 1 : 0));
      }
      refresh();
}

static inline void reload_dict(void)
{
      bkgdset(COLOR_PAIR(COLOR_BACK));
      attron(A_BLINK);
      strncpy(line1, _("Please wait while I'm reloading the dictionary..."), BUFSIZE);
      mvaddnstr(16, LEFT, line1, sx - LEFT);
      attroff(A_BLINK);
      clrtoeol();
      move(17, LEFT);
      clrtoeol();
      *line2 = 0;
      move(sy - 1, sx - 1);
      refresh();
      save_wordlist();
      load_wordlist();
      snprintf(line1, BUFSIZE, _("%u of %u phrases reloaded."), get_wordcount(), get_dictcount());
      mvaddnstr(16, LEFT, line1, sx - LEFT);
      clrtoeol();
      flushinp();
}

void init_screen(int init)
{
      int i, x, y;

      if (init) {
            initscr();
            noecho();
            nonl();
            cbreak();
            keypad(stdscr, TRUE);
            if (has_colors()) {
                  start_color();
                  init_pair(COLOR_BACK,  COLOR_WHITE, COLOR_BLUE);
                  init_pair(COLOR_LINE,  COLOR_BLACK, COLOR_WHITE);
                  init_pair(COLOR_KEY,   COLOR_WHITE, COLOR_WHITE);
                  init_pair(COLOR_INPUT, COLOR_WHITE, COLOR_BLACK);
            }
            if (language[0] == NULL)
                  language[0] = STRDUP(_("Language 1"));
            if (language[1] == NULL)
                  language[1] = STRDUP(_("Language 2"));
      }
      getmaxyx(stdscr, sy, sx);
      bkgdset(COLOR_PAIR(COLOR_BACK));
      clear();
      mvaddnstr(10, LEFT, language[0], MAX_LANG);
      getyx(stdscr, y, max_lang);
      mvaddnstr(12, LEFT, language[1], MAX_LANG);
      getyx(stdscr, y, x);
      if (max_lang < x)
            max_lang = x;
      max_lang -= LEFT;
      if (max_lang > MAX_LANG)
            max_lang = MAX_LANG;
      update_counter(0);
      if (init) {
            attron(A_BLINK);
            strncpy(line1, _("Please wait while I'm loading the dictionary..."), BUFSIZE);
            mvaddnstr(16, LEFT, line1, sx - LEFT);
            attroff(A_BLINK);
            *line2 = 0;
      } else {
            mvaddnstr(16, LEFT, line1, sx - LEFT);
            mvaddnstr(17, LEFT, line2, sx - LEFT);
      }
      makestatuslines();
      bkgdset(COLOR_PAIR(COLOR_INPUT));
      getyx(stdscr, y, x);
      move(10, STARTINPUT - 1);
      for (i = STARTINPUT - 1; i <= STARTINPUT + MAXINPUT; i++)
            addch(' ');
      move(12, STARTINPUT - 1);
      for (i = STARTINPUT - 1; i <= STARTINPUT + MAXINPUT; i++)
            addch(' ');
      move(sy - 1, sx - 1);
      refresh();
}

#define RUNNING  0
#define CONTINUE 1
#define NEW_WORD 2


#ifndef get_wch
int get_wch(wint_t*);
#endif

static int key_handler(wchar_t *answer)
{
      char buf2[BUFSIZE];
      int ret = RUNNING;
      static wchar_t alpha = 0;
      static int zdigraph = 0; /* 0: no digraph; 1: digraph possible; 2: digraph active */
      wint_t c, r;
      pid_t pid;

      memset(buf2, 0, BUFSIZE);
      nodelay(stdscr, 0);
      while ((r = get_wch(&c)) != ERR) {
            nodelay(stdscr, 1);
            switch (r) {
            case KEY_CODE_YES:
                  switch (c) {
                  case KEY_RESIZE:
                        init_screen(0);
                        update_phrase();
                        break;
                  case KEY_BACKSPACE:
                        wcsbackspace(answer, &alpha);
                        zdigraph = (zdigraph == 1) ? 2 : 0;
                        break;
                  case KEY_LEFT:
                        phrase_offset--;
                        zdigraph = 0;
                        update_phrase();
                        break;
                  case KEY_RIGHT:
                        phrase_offset++;
                        zdigraph = 0;
                        update_phrase();
                        break;
                  case KEY_F1:
                        endwin();
                        if ((pid = fork()) == 0)
                              execlp("man", "man", "1", "latrine", NULL);
                        assert(pid > 0); /* FIXME: errmsg */
                        /* FIXME: close(stdin) etc.? */
                        waitpid(pid, NULL, 0);
                        refresh();
                        break;
                  case KEY_F2:
                        reload_dict();
                        zdigraph = 0;
                        ret = NEW_WORD;
                        break;
                  case KEY_F3:
                        update_mode(1);
                        break;
                  case KEY_F4:
                        update_dir(1);
                        update_phrase();
                        break;
                  default:
                        if (debug)
                              fprintf(debug, "key_handler: unknown key code: %d\n", c);
                  }
                  break;
            case OK:
                  switch (c) {
                  case KEY_ESC:
                        exit(0);
                  case KEY_RETURN:
                        zdigraph = 0;
                        ret = CONTINUE;
                        break;
                  case KEY_CTRL_L:
                        init_screen(0);
                        update_phrase();
                        break;
                  case KEY_CTRL_U:
                        *answer = 0L;
                        zdigraph = 0;
                        break;
                  case KEY_TAB:
                        zdigraph = 0;
                        ret = NEW_WORD;
                        break;
                  default:
                        if (digraph && (zdigraph == 2))
                              make_digraph(alpha, &c);
                        if (wcsappend(reverse ? 0 : 1, answer, c, BUFSIZE) == -1) {
                              wcsbackspace(answer, NULL);
                              wcsappend(reverse ? 0 : 1, answer, c, BUFSIZE);
                        }
                        zdigraph = 1;
                  }
                  break;
            }
      }
      update_translation(answer);
      return ret;
}

static int check(wchar_t* answer)
{
      char buffer[BUFSIZE];
      char *c;
      struct Tlang *l;

      if (direction / 2 != direction % 2)
            wcsreverse(answer);
      wcstombs(buffer, answer, BUFSIZE);

      for (l = p->lang[reverse ? 0 : 1]; l != NULL; l = l->next) {
            c = buffer;
            while (1) {
                  if (ignore_case) {
                        if ((c = strcasestr(c, l->c)) == NULL)
                              return 0;
                  } else {
                        if ((c = strstr(c, l->c)) == NULL)
                              return 0;
                  }
                  if ((c == buffer || *(c - 1) == ' ') &&
                        (*(c + strlen(l->c)) == ',' || *(c + strlen(l->c)) == 0))
                        break;
                  c++;
            }
      }
      return 1;
}

static inline void blowup(char **s, const size_t n)
{
      char *p = NULL;
      if (strlen(*s) < n) {
            p = (char*)MALLOC(n + 1);
            memset(p, ' ', n);
            memcpy(p, *s, strlen(*s));
            p[n] = 0;
            *s = p;
      }
}

void running(void)
{
      wchar_t answer[BUFSIZE];
      char *correct = NULL;
      char *wrong   = NULL;
      char *history = NULL;
      int i, msgsize;
      struct Tlang *l;

      correct = _("correct");
      msgsize = strlen(correct);
      wrong   = _("wrong");
      if (strlen(wrong) > msgsize)
            msgsize = strlen(wrong);
      history = _("history");
      if (strlen(history) > msgsize)
            msgsize = strlen(history);
      blowup(&correct, msgsize);
      blowup(&wrong,   msgsize);
      blowup(&history, msgsize);

      bkgdset(COLOR_PAIR(COLOR_BACK));
      snprintf(line1, BUFSIZE, _("%u of %u phrases loaded."), get_wordcount(), get_dictcount());
      mvaddnstr(16, LEFT, line1, sx - LEFT);
      clrtoeol();
      flushinp();
      while ((p = select_word()) != NULL) {
            assert(p);
            assert(p->lang[0]->c);
            assert(p->lang[1]->c);
            reverse = mode == MODE_REVERSE || (mode == MODE_MIXED && random() & 1);
            update_phrase();
            update_translation(NULL);
            *answer = 0L;
            while ((i = key_handler(answer)) == RUNNING);
            if (i != CONTINUE)
                  continue;
            i = check(answer); /* may destroy answer */
            update_counter(i ? 1 : -1);
            update_word(p, i);
            snprintf(line1, BUFSIZE, "%s: %s", i ? correct : wrong, p->lang[reverse ? 1 : 0]->c);
            for (l = p->lang[reverse ? 1 : 0]->next; l != NULL; l = l->next) {
                  strncat(line1 + strlen(line1), ", ", sizeof(line1) - strlen(line1) - 1);
                  strncat(line1 + strlen(line1), l->c, sizeof(line1) - strlen(line1) - 1);
            }
            strncat(line1 + strlen(line1), " -> ", sizeof(line1) - strlen(line1) - 1);
            strncat(line1 + strlen(line1), p->lang[reverse ? 0 : 1]->c, sizeof(line1) - strlen(line1) - 1);
            for (l = p->lang[reverse ? 0 : 1]->next; l != NULL; l = l->next) {
                  strncat(line1 + strlen(line1), ", ", sizeof(line1) - strlen(line1) - 1);
                  strncat(line1 + strlen(line1), l->c, sizeof(line1) - strlen(line1) - 1);
            }
            bkgdset(COLOR_PAIR(COLOR_BACK));
            mvaddnstr(16, LEFT, line1, sx - LEFT);
            clrtoeol();
            snprintf(line2, BUFSIZE, "%s: |%s|", history, p->history);
            mvaddnstr(17, LEFT, line2, sx - LEFT);
            clrtoeol();
      }
}

Generated by  Doxygen 1.6.0   Back to index