import java.applet.*; import java.awt.*; public class TypeLesson extends Applet implements Runnable { Thread thread; boolean needUpdate = true; // キーボード表示部 static final int XMIN = 20, YMIN = 30, XSTEP = 30, YSTEP = 30; // QWERTY 配列の表 String[][] str = { {"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "@", "["}, {"A", "S", "D", "F", "G", "H", "J", "K", "L", ";", ":", "]"}, {"Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "\\", "\0"}}; char[][] ch = new char[3][12]; // 上記の char 版 int[][] xpos = new int[3][12]; // 上記の文字を表示する x 座標 int[] ypos = new int[3]; // 上記の文字を表示する y 座標 // Enter, Space の文字列を表示する x, y 座標 int enterxpos, enterypos, spacexpos, spaceypos; char key; // 打ったキー Font keyface15, keyface24; // キーボードの刻印のための文字 // 問題表示部 Font times24, default15, default24, default50; final String[] ss = new String[3]; // 問題の文字列 StringBuffer[] cs = new StringBuffer[3]; // 反応の文字列 StringBuffer es = new StringBuffer(100); // エラー保管用 int line, lines, count, error, totalcount, tolerance, maxtime; int[] ipos = new int[128]; int[] jpos = new int[128]; long startTime, endTime; int buttonstrx, buttonstry; // ボタンの中の文字列の座標 public void init() { String defaultfont = getFont().getName(); default15 = new Font(defaultfont, Font.PLAIN, 15); default24 = new Font(defaultfont, Font.PLAIN, 24); default50 = new Font(defaultfont, Font.PLAIN, 50); times24 = new Font("TimesRoman", Font.PLAIN, 24); // times24 = new Font("Courier", Font.BOLD, 20); setBackground(Color.white); try { tolerance = Integer.parseInt(getParameter("error")); maxtime = Integer.parseInt(getParameter("time")); } catch (Exception e) { tolerance = maxtime = 0; } for (lines = 0; lines < 3; lines++) { cs[lines] = new StringBuffer(100); if (maxtime != 0) { ss[lines] = getParameter("ss" + lines); if (ss[lines] == null) break; } else { ss[lines] = ""; } totalcount += ss[lines].length(); } totalcount += lines; maxtime *= totalcount; keyface15 = new Font("Helvetica", Font.BOLD, 15); keyface24 = new Font("Helvetica", Font.BOLD, 24); Graphics g = getGraphics(); g.setFont(keyface24); FontMetrics fm = g.getFontMetrics(); int a = (YSTEP + fm.getAscent() - fm.getDescent()) / 2; for (int i = 0; i < 128; i++) ipos[i] = jpos[i] = -1; for (int i = 0; i < 3; i++) { for (int j = 0; j < 12; j++) { ch[i][j] = str[i][j].charAt(0); xpos[i][j] = XMIN + XSTEP * j + (XSTEP * (i + 1) - fm.charWidth(ch[i][j])) / 2; ypos[i] = YMIN + YSTEP * i + a; ipos[(int) ch[i][j]] = i; jpos[(int) ch[i][j]] = j; } } g.setFont(keyface15); fm = g.getFontMetrics(); enterxpos = XMIN + XSTEP * 12 + XSTEP * 3 / 4 - fm.stringWidth("Enter") / 2; enterypos = ypos[0]; spacexpos = XMIN + XSTEP * 5 + (XSTEP - fm.stringWidth("Space")) / 2; spaceypos = YMIN + YSTEP * 3 + (YSTEP + fm.getAscent() - fm.getDescent()) / 2; g.setFont(default15); fm = g.getFontMetrics(); buttonstrx = XMIN + XSTEP * 13 + (XSTEP - fm.stringWidth("はじめ")) / 2; buttonstry = YMIN + YSTEP * 3 + (YSTEP + fm.getAscent() - fm.getDescent()) / 2; startLesson(); } public void start() { if (thread == null) { thread = new Thread(this); thread.start(); } } public void stop() { if (thread != null) { thread.stop(); thread = null; } } public void startLesson() { for (int i = 0; i < 3; i++) cs[i].setLength(0); es.setLength(0); line = count = error = 0; startTime = endTime = 0; key = (char) 0; } static final void zigzag(Graphics g, int x, int y) { g.drawLine(x, y, x, y + YSTEP); g.drawLine(x, y + YSTEP, x + XSTEP / 2, y + YSTEP); g.drawLine(x + XSTEP / 2, y + YSTEP, x + XSTEP / 2, y + YSTEP * 2); g.drawLine(x + XSTEP / 2, y + YSTEP * 2, x + XSTEP, y + YSTEP * 2); g.drawLine(x + XSTEP, y + YSTEP * 2, x + XSTEP, y + YSTEP * 3); } public void paint(Graphics g) { int i, j; g.setColor(Color.black); g.drawLine(XMIN, YMIN, XMIN + XSTEP * 13 + XSTEP / 2, YMIN); g.drawLine(XMIN + XSTEP, YMIN + YSTEP * 3, XMIN + XSTEP * 12, YMIN + YSTEP * 3); g.drawLine(XMIN + XSTEP * 13 + XSTEP / 2, YMIN, XMIN + XSTEP * 13 + XSTEP / 2, YMIN + YSTEP * 2); g.drawLine(XMIN + XSTEP * 12, YMIN + YSTEP * 2, XMIN + XSTEP * 13 + XSTEP / 2, YMIN + YSTEP * 2); g.drawLine(XMIN + XSTEP * 12, YMIN + YSTEP * 2, XMIN + XSTEP * 12, YMIN + YSTEP * 3); g.drawRect(XMIN + XSTEP * 3 + XSTEP / 2, YMIN + YSTEP * 3, XSTEP * 4, YSTEP); for (i = 0; i < 4; i++) zigzag(g, XMIN + XSTEP * i, YMIN); zigzag(g, XMIN + XSTEP * 5, YMIN); for (i = 7; i < 10; i++) zigzag(g, XMIN + XSTEP * i, YMIN); g.setColor(Color.blue); g.setFont(keyface24); for (j = 0; j < 12; j++) g.drawString(str[0][j], xpos[0][j], ypos[0]); g.drawString(str[1][4], xpos[1][4], ypos[1]); g.drawString(str[1][5], xpos[1][5], ypos[1]); g.drawString(str[1][10], xpos[1][10], ypos[1]); g.drawString(str[1][11], xpos[1][11], ypos[1]); for (j = 0; j < 11; j++) g.drawString(str[2][j], xpos[2][j], ypos[2]); g.setFont(keyface15); g.drawString("Enter", enterxpos, enterypos); g.drawString("Space", spacexpos, spaceypos); g.setFont(keyface24); g.setColor(Color.orange); for (j = 0; j < 4; j++) g.drawString(str[1][j], xpos[1][j], ypos[1]); for (j = 6; j < 10; j++) g.drawString(str[1][j], xpos[1][j], ypos[1]); g.setColor(Color.red); if (key >= 128) { // do nothing } else if ((i = ipos[key]) >= 0) { j = jpos[key]; g.drawString(str[i][j], xpos[i][j], ypos[i]); } else if (key == 10 || key == 13) { g.setFont(keyface15); g.drawString("Enter", enterxpos, enterypos); } else if (key == ' ') { g.setFont(keyface15); g.drawString("Space", spacexpos, spaceypos); } g.setColor(Color.lightGray); g.fill3DRect(XMIN + XSTEP * 12 + XSTEP / 2, YMIN + YSTEP * 3, XSTEP * 2, YSTEP, true); g.setFont(default15); g.setColor(Color.black); g.drawString("はじめ", buttonstrx, buttonstry); g.setFont(times24); for (i = 0; i < lines; i++) g.drawString(ss[i], 10, 180 + 24 * i); g.setColor(Color.blue); for (i = 0; i < lines; i++) g.drawString(cs[i].toString(), 10, 180 + 24 * i); g.setColor(Color.red); g.drawString(es.toString(), 10, 264); if (endTime != 0) { g.setColor(Color.black); int percent = (100 * error + totalcount / 2) / totalcount; int time = (int)(endTime - startTime); g.setFont(default24); g.drawString("誤打: " + error + " (" + percent + "%)", 30, 300); g.drawString("時間: " + (time / 1000) + "." + ((time / 100) % 10) + " 秒", 30, 324); g.setFont(default50); if (maxtime <= 0 || tolerance <= 0) { // do nothing } else if (time <= maxtime && percent <= tolerance) { g.setColor(Color.blue); g.drawString("合格!!", 280, 324); } else { g.setColor(Color.red); g.drawString("残念(^^;)", 280, 324); } } g.setFont(times24); } public void update(Graphics g) { paint(g); // System.out.println("Updated"); } public boolean keyDown(Event e, int k) { // System.out.println("Key pressed: " + k); // Enter は 10 で Ctrl-M は keyDown() を呼び出さない // BackSpace も Ctrl-H も 8 になる。 char c = (char) k; key = Character.toUpperCase(c); if (endTime != 0) { // do nothing } else if (maxtime <= 0) { if (k != 10 & k != 13) { cs[line].append(c); } else if (line < lines - 1) { line++; } else { StringBuffer s = cs[0]; s.setLength(0); cs[0] = cs[1]; cs[1] = cs[2]; cs[2] = s; Graphics g = getGraphics(); g.clearRect(0, 160, 550, 190); } } else if (k == 10 || k == 13) { if (cs[line].length() != ss[line].length()) { error++; es.append('|'); } else { count = 0; if (++line == lines) endTime = System.currentTimeMillis(); } } else if (count < ss[line].length() && c == ss[line].charAt(count)) { if (startTime == 0) startTime = System.currentTimeMillis(); cs[line].append(c); count++; } else { error++; if (c == ' ') es.append('_'); else es.append(c); } needUpdate = true; return true; } public boolean mouseDown(Event e, int x, int y) { if (x >= XMIN + XSTEP * 13 && x <= XMIN + XSTEP * 15 && y >= YMIN + YSTEP * 3 && y <= YMIN + YSTEP * 4) { Graphics g = getGraphics(); g.clearRect(0, 0, 550, 350); startLesson(); needUpdate = true; return true; } return false; } public void run() { while (true) { Thread.yield(); if (needUpdate) { needUpdate = false; update(getGraphics()); } } } }