mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
ktrans: gui and man page rework
Graphical display shows current candidate list.
This commit is contained in:
parent
a3f3953ab2
commit
88b9bda96c
2 changed files with 247 additions and 58 deletions
137
sys/man/1/ktrans
137
sys/man/1/ktrans
|
@ -4,6 +4,9 @@ ktrans \- language transliterator
|
|||
.SH SYNOPSIS
|
||||
.B ktrans
|
||||
[
|
||||
.B -G
|
||||
]
|
||||
[
|
||||
.B -l
|
||||
.I lang
|
||||
]
|
||||
|
@ -21,46 +24,101 @@ If a
|
|||
.I kbdtap
|
||||
file is given, it is used for both
|
||||
input and output instead.
|
||||
.I Ktrans
|
||||
starts in a passthrough mode, echoing out
|
||||
the input with no conversions. Control characters
|
||||
are used to give instructions, the following
|
||||
control sequences are used to switch between languages:
|
||||
.TP
|
||||
.B ctl-t
|
||||
English (Passthrough).
|
||||
.TP
|
||||
.B ctl-n
|
||||
Japanese Hiragana.
|
||||
.TP
|
||||
.B ctl-k
|
||||
Japanese Katakana.
|
||||
.TP
|
||||
.B ctl-c
|
||||
Chinese.
|
||||
.TP
|
||||
.B ctl-r
|
||||
Russian.
|
||||
.TP
|
||||
.B ctl-o
|
||||
Greek.
|
||||
.TP
|
||||
.B ctl-s
|
||||
Korean.
|
||||
.TP
|
||||
.B ctl-v
|
||||
Vietnamese.
|
||||
.SH CONVERSION
|
||||
Conversion is done in two layers, an implicit
|
||||
layer for unambigious mappings, and an explicit
|
||||
layer for selecting one match out of a list of
|
||||
ambigious matches.
|
||||
ambigious matches. The following control characters
|
||||
are used for conversion instructions.
|
||||
.TP
|
||||
.B ctl-\e
|
||||
Explicitely match the current input, consecutive inputs of ctl-\e
|
||||
will cycle through all the possible options.
|
||||
.TP
|
||||
.B ctl-l
|
||||
Reset the current input buffer.
|
||||
.PP
|
||||
The implicit layer happens automatically as characters
|
||||
are input, transforming a consecutive set of key strokes
|
||||
in to their rune counterpart. A series of these runes can
|
||||
then be explicitely converted using ctrl-\\. Consecutive
|
||||
inputs of ctrl-\\ can then be used to cycle through all the
|
||||
matches. A newline may also be used to perform an explicit
|
||||
conversion, but will not cycle through other possible matches.
|
||||
in to their rune counterpart. A series of runes may then
|
||||
be explicitely matched by cycling through a list of options.
|
||||
.I Ktrans
|
||||
automatically maintains a buffer of the current series of
|
||||
key strokes being considered for an explicit match, and resets
|
||||
that buffer on logical "word" breaks depending on the language.
|
||||
However manual hints of when to reset this buffer will likely
|
||||
still be required.
|
||||
.PP
|
||||
Input is always passed along, when a match is found
|
||||
.I Ktrans
|
||||
will emit backspaces to clear the input sequence and replace
|
||||
it with the matched sequence.
|
||||
.SH CONTROL
|
||||
The language is selected by typing a control character:
|
||||
.SH DISPLAY
|
||||
.I Ktrans
|
||||
will provide a graphical display of current explicit conversion
|
||||
candidates as implicit conversion is done. Candidates are highlighted
|
||||
as a user cycles through them. At the bottom of the list is an exit
|
||||
button for quiting the program. Keyboard input typed in to the window is
|
||||
transliterated but discarded, providing a scratch input space. The
|
||||
.B -G
|
||||
option disables this display.
|
||||
.SH JAPANESE
|
||||
The Hiragana and Katakana modes implicitly turn hepburn representations
|
||||
in to their Kana counterparts. Explicit conversions combine sequences
|
||||
of Hiragana in to Kanji.
|
||||
.PP
|
||||
The
|
||||
.B /sys/lib/kbmap/jp
|
||||
keyboard map will turn the language input keys
|
||||
present on OADG 109(A) keyboards in to control
|
||||
sequences matching their label:
|
||||
.TP
|
||||
.B ctl-t
|
||||
Passthrough mode
|
||||
.B Henkan
|
||||
Convert to Kanji (ctl-\e)
|
||||
.TP
|
||||
.B ctl-n
|
||||
Japanese mode. Implicit layer converts hepburn sequences to hiragana. Explicit
|
||||
layer converts sequences of hiragana with optional trailing particle or okurigana.
|
||||
.B Muhenkan
|
||||
Clear Kanji buffer (ctl-l)
|
||||
.TP
|
||||
.B ctl-k
|
||||
Implicit only Japanese Katakana layer.
|
||||
.B Hiragana / Katakana
|
||||
Switch to Hiragana (ctl-n)
|
||||
.TP
|
||||
.B ctrl-c
|
||||
Chinese Wubi mode. No implicit conversion is done. Explicit layer
|
||||
converts sequences of latin characters to hanzi.
|
||||
.TP
|
||||
.B ctl-l
|
||||
Clear the explicit layer's current input sequence.
|
||||
.TP
|
||||
.B ctl-r
|
||||
Russian mode. Implicit layer converts latin to Cyrillic; the transliteration is mostly
|
||||
.B Shift + Hiragana / Katakana
|
||||
Switch to Katakana (ctl-v)
|
||||
.SH CHINESE
|
||||
The Wubizixing input method is used. No implicit conversion is done,
|
||||
explicit conversion interprets latin characters as their Wubi counterparts
|
||||
to do lookup of Hanzi.
|
||||
.SH RUSSIAN
|
||||
Implicit layer converts latin to Cyrillic; the transliteration is mostly
|
||||
phonetic, with
|
||||
.B '
|
||||
for
|
||||
|
@ -76,15 +134,13 @@ for ё,
|
|||
for
|
||||
.IR i-kratkaya
|
||||
(й).
|
||||
.TP
|
||||
.B ctl-o
|
||||
Greek mode.
|
||||
.TP
|
||||
.B ctl-s
|
||||
Korean mode. Implicit layer converts latin to Korean Hangul.
|
||||
.TP
|
||||
.B ctrl-v
|
||||
Vietnamese Telex input.
|
||||
.SH VIETNAMESE
|
||||
Implicit conversion is modeled after Telex, supporting
|
||||
standard diacritic suffixes.
|
||||
.SH KOREAN
|
||||
Mapping is done by emulating a Dubeolsik layout, with each latin
|
||||
character mapping to a single Jamo. Sequences of up to three Jamo
|
||||
are automatically converted to Hangul syllables.
|
||||
.SH EXAMPLES
|
||||
To type the following Japanese text:
|
||||
|
||||
|
@ -95,14 +151,13 @@ To type the following Japanese text:
|
|||
|
||||
your keyboard typing stream should be:
|
||||
|
||||
watashiHA[^\\]mainichi[^\\]35[^l]fun[^\\]ijou[^\\]aruIte,[^\\]
|
||||
saraNI[^\\]10[^l]fun[^\\]denshaNI[^\\]noTte[^\\]gakkouNI[^\\]
|
||||
kayoImasu.[\\n]kenkouNO[^\\]ijiNImo[^\\]yakuDAtteimasuga,[^\\]
|
||||
nakanakatanoshiImonodesu.[\\n]
|
||||
watashiHA[^\e]mainichi[^\e]35[^l]fun[^\e]ijou[^\e]aruIte,[^\e]
|
||||
saraNI[^\e]10[^l]fun[^\e]denshaNI[^\e]noTte[^\e]gakkouNI[^\e]
|
||||
kayoImasu.[\en]kenkouNO[^\e]ijiNImo[^\e]yakuDAtteimasuga,[^\e]
|
||||
nakanakatanoshiImonodesu.[\en]
|
||||
|
||||
where [^\\] and [^l] indicate 'ctl-\\' and 'ctl-l',
|
||||
respectively. See README.kenji for the details of this Japanese input
|
||||
method.
|
||||
where [^\e] and [^l] indicate 'ctl-\e' and 'ctl-l',
|
||||
respectively.
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/ktrans
|
||||
.SH SEE ALSO
|
||||
|
@ -115,6 +170,8 @@ method.
|
|||
.SH BUGS
|
||||
.PP
|
||||
There is no way to generate the control characters literally.
|
||||
Plan9 lacks support for rendering combinational Unicode sequences,
|
||||
limiting the use of some code ranges.
|
||||
.SH HISTORY
|
||||
Ktrans was originally written by Kenji Okamoto in August of 2000 for
|
||||
the 2nd edition of Plan 9. It was imported in to 9front in July of
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include <bio.h>
|
||||
#include <plumb.h>
|
||||
#include <thread.h>
|
||||
#include <draw.h>
|
||||
#include <mouse.h>
|
||||
#include <keyboard.h>
|
||||
#include "hash.h"
|
||||
|
||||
char*
|
||||
|
@ -282,6 +285,113 @@ static Channel *output;
|
|||
static Channel *input;
|
||||
static char backspace[Msgsize];
|
||||
|
||||
static Channel *displaych;
|
||||
static Channel *selectch;
|
||||
|
||||
static void
|
||||
displaythread(void*)
|
||||
{
|
||||
Mousectl *mctl;
|
||||
Mouse m;
|
||||
Keyboardctl *kctl;
|
||||
Rune key;
|
||||
char *kouho[16+1+1], **s;
|
||||
Image *back, *text, *board, *high;
|
||||
Font *f;
|
||||
Point p;
|
||||
Rectangle r, exitr, selr;
|
||||
int selected;
|
||||
enum { Adisp, Aresize, Amouse, Asel, Akbd, Aend };
|
||||
Alt a[] = {
|
||||
[Adisp] { nil, kouho+1, CHANRCV },
|
||||
[Aresize] { nil, nil, CHANRCV },
|
||||
[Amouse] { nil, &m, CHANRCV },
|
||||
[Asel] { nil, &selected, CHANRCV },
|
||||
[Akbd] { nil, &key, CHANRCV },
|
||||
[Aend] { nil, nil, CHANEND },
|
||||
};
|
||||
|
||||
if(initdraw(nil, nil, "ktrans") < 0)
|
||||
sysfatal("failed to initdraw: %r");
|
||||
|
||||
mctl = initmouse(nil, screen);
|
||||
if(mctl == nil)
|
||||
sysfatal("failed to get mouse: %r");
|
||||
|
||||
/*
|
||||
* For keys coming in to our specific window.
|
||||
* We've already transliterated these, but should
|
||||
* consume keys and exit on del to avoid artifacts.
|
||||
*/
|
||||
kctl = initkeyboard(nil);
|
||||
if(kctl == nil)
|
||||
sysfatal("failed to get keyboard: %r");
|
||||
|
||||
memset(kouho, 0, sizeof kouho);
|
||||
kouho[0] = "候補";
|
||||
selected = -1;
|
||||
f = display->defaultfont;
|
||||
high = allocimagemix(display, DYellowgreen, DWhite);
|
||||
text = display->black;
|
||||
back = allocimagemix(display, DPaleyellow, DWhite);
|
||||
board = allocimagemix(display, DBlack, DWhite);
|
||||
|
||||
a[Adisp].c = displaych;
|
||||
a[Aresize].c = mctl->resizec;
|
||||
a[Amouse].c = mctl->c;
|
||||
a[Asel].c = selectch;
|
||||
a[Akbd].c = kctl->c;
|
||||
|
||||
threadsetname("display");
|
||||
goto Redraw;
|
||||
for(;;)
|
||||
switch(alt(a)){
|
||||
case Akbd:
|
||||
if(key != Kdel)
|
||||
break;
|
||||
closedisplay(display);
|
||||
threadexitsall(nil);
|
||||
case Amouse:
|
||||
if(!m.buttons)
|
||||
break;
|
||||
if(!ptinrect(m.xy, exitr))
|
||||
break;
|
||||
closedisplay(display);
|
||||
threadexitsall(nil);
|
||||
case Aresize:
|
||||
getwindow(display, Refnone);
|
||||
case Adisp:
|
||||
Redraw:
|
||||
r = screen->r;
|
||||
draw(screen, r, back, nil, ZP);
|
||||
r.max.y = r.min.y + f->height;
|
||||
draw(screen, r, board, nil, ZP);
|
||||
|
||||
if(selected+1 > 0 && kouho[selected+1] != nil){
|
||||
selr = screen->r;
|
||||
selr.min.y += f->height*(selected+1);
|
||||
selr.max.y = selr.min.y + f->height;
|
||||
draw(screen, selr, high, nil, ZP);
|
||||
}
|
||||
|
||||
r.min.x += Dx(r)/2;
|
||||
p.y = r.min.y;
|
||||
for(s = kouho; *s != nil; s++){
|
||||
p.x = r.min.x - stringwidth(f, *s)/2;
|
||||
string(screen, p, text, ZP, f, *s);
|
||||
p.y += f->height;
|
||||
}
|
||||
|
||||
p.x = r.min.x - stringwidth(f, "出口")/2;
|
||||
p.y = screen->r.max.y - f->height;
|
||||
exitr = Rpt(Pt(0, p.y), screen->r.max);
|
||||
draw(screen, exitr, board, nil, ZP);
|
||||
string(screen, p, text, ZP, f, "出口");
|
||||
flushimage(display, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
emitutf(Channel *out, char *u, int nrune)
|
||||
{
|
||||
|
@ -326,13 +436,14 @@ dictthread(void*)
|
|||
for(p = m+1; *p; p += n){
|
||||
n = chartorune(&r, p);
|
||||
if(r != ''){
|
||||
selected = -1;
|
||||
kouho[0] = nil;
|
||||
if(selected >= 0){
|
||||
resetstr(&okuri, nil);
|
||||
mode = Kanji;
|
||||
send(selectch, &selected);
|
||||
}
|
||||
resetstr(&last, nil);
|
||||
selected = -1;
|
||||
kouho[0] = nil;
|
||||
}
|
||||
switch(r){
|
||||
case LangJP:
|
||||
|
@ -352,6 +463,8 @@ dictthread(void*)
|
|||
case '':
|
||||
mode = Kanji;
|
||||
resetstr(&line, &okuri, nil);
|
||||
memset(kouho, 0, sizeof kouho);
|
||||
send(displaych, kouho);
|
||||
break;
|
||||
case '\b':
|
||||
if(mode != Kanji){
|
||||
|
@ -389,6 +502,8 @@ dictthread(void*)
|
|||
selected = -1;
|
||||
break;
|
||||
}
|
||||
send(selectch, &selected);
|
||||
send(displaych, kouho);
|
||||
|
||||
if(okuri.p != okuri.b)
|
||||
emitutf(output, backspace, utflen(okuri.b));
|
||||
|
@ -405,16 +520,11 @@ dictthread(void*)
|
|||
mode = Kanji;
|
||||
break;
|
||||
default:
|
||||
if(dict == zidian){
|
||||
line.p = pushutf(line.p, strend(&line), p, 1);
|
||||
break;
|
||||
}
|
||||
if(dict == zidian)
|
||||
goto Line;
|
||||
if(mode == Joshi)
|
||||
goto Okuri;
|
||||
|
||||
if(mode == Joshi){
|
||||
okuri.p = pushutf(okuri.p, strend(&okuri), p, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if(isupper(*p)){
|
||||
if(mode == Okuri){
|
||||
popstr(&line);
|
||||
|
@ -424,14 +534,22 @@ dictthread(void*)
|
|||
}
|
||||
mode = Okuri;
|
||||
*p = tolower(*p);
|
||||
line.p = pushutf(line.p, strend(&line), p, 1);
|
||||
okuri.p = pushutf(okuri.b, strend(&okuri), p, 1);
|
||||
break;
|
||||
goto Line;
|
||||
}
|
||||
if(mode == Kanji)
|
||||
line.p = pushutf(line.p, strend(&line), p, 1);
|
||||
else
|
||||
if(mode != Kanji){
|
||||
Okuri:
|
||||
okuri.p = pushutf(okuri.p, strend(&okuri), p, 1);
|
||||
break;
|
||||
}
|
||||
Line:
|
||||
line.p = pushutf(line.p, strend(&line), p, 1);
|
||||
memset(kouho, 0, sizeof kouho);
|
||||
if(hmapget(dict, line.b, kouho) == 0){
|
||||
selected = -1;
|
||||
send(selectch, &selected);
|
||||
}
|
||||
send(displaych, kouho);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -672,7 +790,7 @@ plumbproc(void*)
|
|||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s [ -l lang ] [ kbdtap ]\n", argv0);
|
||||
fprint(2, "usage: %s [ -G ] [ -l lang ] [ kbdtap ]\n", argv0);
|
||||
threadexits("usage");
|
||||
}
|
||||
|
||||
|
@ -681,16 +799,20 @@ mainstacksize = 8192*2;
|
|||
void
|
||||
threadmain(int argc, char *argv[])
|
||||
{
|
||||
|
||||
int nogui;
|
||||
char *jishoname, *zidianname;
|
||||
|
||||
deflang = LangEN;
|
||||
nogui = 0;
|
||||
ARGBEGIN{
|
||||
case 'l':
|
||||
deflang = parselang(EARGF(usage()));
|
||||
if(deflang < 0)
|
||||
usage();
|
||||
break;
|
||||
case 'G':
|
||||
nogui++;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND;
|
||||
|
@ -708,6 +830,16 @@ threadmain(int argc, char *argv[])
|
|||
usage();
|
||||
}
|
||||
|
||||
/* allow gui to warm up while we're busy reading maps */
|
||||
if(nogui || access("/dev/winid", AEXIST) < 0){
|
||||
displaych = nil;
|
||||
selectch = nil;
|
||||
} else {
|
||||
selectch = chancreate(sizeof(int), 1);
|
||||
displaych = chancreate(sizeof(char*)*16, 1);
|
||||
proccreate(displaythread, nil, mainstacksize);
|
||||
}
|
||||
|
||||
memset(backspace, '\b', sizeof backspace-1);
|
||||
backspace[sizeof backspace-1] = '\0';
|
||||
|
||||
|
|
Loading…
Reference in a new issue