[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[thhsieh: xcin-2.5 GUI Request]



----- Forwarded message from thhsieh -----

Date: Fri, 18 Feb 2000 23:23:36 +0800
From: thhsieh
To: xcin@linux.org.tw
Subject: xcin-2.5 GUI Request
X-Mailer: Mutt 1.0pre3us


本文讨论输入法模组的 GUI Request: Menu Selection window 的 API 介面。
阅读本文时,建议您可以同时参考:

http://xcin.linux.org.tw/xcin-2.5/xcin_menu.png

可以帮助您理解 :-))


1. 简介:

自 xcin-2.5.2 起输入法模组开始支援 GUI Request 功能,它是提供输入法模组
开启其他视窗以显示资料的方式。由于 xcin 主视窗的空间有限,这对于需要显
示复杂资讯的输入法模组而言可能不够用,因而有这样的设计。

GUI Request 是与 winlist 架构整合在一起的,它可以是各种型态或功能的视窗,
凡是可供输入法模组用来显示其资讯的,我都称为 GUI Request 视窗。其在 winlist
的定义中, wid 是设成 WID_OVERSPOT 的 (见 include/gui.h)。

而在目前,我只设计了一个 GUI Request 的视窗供输入法模组使用,我称它为
Menu Selection window (简称 menusel), 它的功能就是显示出一个选单,其
source 就在 gui_menusel.c 中。未来若还有需要,可以再加入其他的 GUI Request
视窗。

输入法模组可以透过 inpinfo 结构 (见 include/module.h) 来启动、操作 GUI 
Request 视窗。由于 inpinfo 结构属于 IMC 的一部分 (有关 IMC 介绍,请见我
上一篇 post: xcin-2.5 & OverTheSpot), 因此,若在 XCIN_SINGLE_IMC OFF 的
状态下,不同的 IMC 可以分别有它自己的一套 GUI Request window 群。这意思
是说,您可以做到在 XIM client A 开启一个 GUI Request window, 而在 client
B 开启另一个 GUI Request window, 二者互相独立。当然,如果在 XCIN_SINGLE_IMC
ON 的状态下,所有的 clients (或 IC) 分享同一个 IMC, 则它底下的 GUI Request
window 也同样被所有的 clients 分享。

在同一个 IMC (或 inpinfo) 下,输入法模组可以开启多个 GUI Request window, 
而且开启的种类并没有限制。当然,目前只有一种,即 menusel, 因此若您高兴的
话,也可以同时开启两三个选单来用。目前我是设定每个 IMC 最多只能开出五个
GUI Request window, 我想这样应该是很够用了吧。

PS. 写到这里,我又发现 xcin-2.5.2-pre1 有 bug, 在 gui.c, update_gui_request()
    中没有检查同一个 IMC 中开启的 GUI Request window 数目是否已达到上限,
    这部分我会马上进行修正。


2. 启动 GUI Request:

在 inpinfo 结构中,有一个 greq_t *qui_request 的指标,它就是用来操作 GUI
Request 用的,其定义如下:

typedef struct greq_s {
    short reqid;			/* request id */
    short type;				/* request type */
    ubyte_t deleted_return;		/* request window is deleted */
    void *data;				/* request data */
    struct greq_s *next;
} greq_t;

其中 type 就是用来指定您要启动那一种 GUI Request window, 对于 menusel 而
言,您要在这里设为 GREQ_MENUSEL。

之前说过,输入法模组可以在同一个 IMC 中开启多个 GUI Request window, 在这
里每一个这样的 window 就是一个 greq_t,然后再一个个用 link list 串起来,
最后的那一个的 next 必须设为 NULL。而不同的 window 就以 reqid 来区分。这
个 reqid 可以是大于等于 1 的任何数字,由输入法模组自己去设,只要每个 greq_t
的 reqid 都不一样即可。

各 GUI Request window 真正的资料结构是存放在 data 所指的区域。由于不同的
GUI Request 其所需的资料结构不见得相同,像 menusel 的需的资料结构就称为
greq_menusel_t (见后述), 因此这里我是以一个 void * 指标来代表,因此您要
使用那一□GUI Request, 就必须将 data 指向正确的资料结构,不可弄错了。
有人曾建议我说这里最好是弄成 union 指标, 但老实说我有点笨,不晓得该怎么
弄 :-)) 所以如有人知道的话请教我一下 :-))

输入法可以自由控制 greq_t 的 window 的产生与关闭 (destroy), 若要产生它,
就多串上一个 greq_t 即可,若要关闭它,就将相对应的 greq_t 从 list 拿掉即
可。 xcin 完全不会替您维护此 list 的内容。因此,若此 inpinfo 要转让给其
他输入法模组使用 (也就是本输入法模组的 xim_end 函式被呼叫时),则必须自己
将已开启的所有 greq_t 拿掉 (或 free 掉),否则的话将会有 memory leakage 
的问题。

但是,在有些情况下 xcin 会主动关掉目前正运作中的 GUI Request window, 例如:

	1. 使用者用滑鼠点了视窗上的按钮将它关掉时。
	2. 使用者突然按 ctrl+alt+... 切换到别的输入法时。

这时候, xcin 只会将 GUI Request window 关掉,但仍然不会去动 inpinfo 里头
的 greq_t, 它只会在已视窗已关掉的 greq_t 里头的 deleted_return 设为 1, 藉
此通知输入法模组说该视窗已关掉了,您必须做后续处理。通常的情况是,输入法
模组必须马上将此 greq_t 自 list 中拿掉。若不去理会,那多半会跑出一些很好
玩的结果,然后我们就会听到使用者的抱怨了 :-)) 因此,每次当输入法模组的
keystroke 函式被执行时,它必须检查每个 greq_t 中的 deleted_return 是不是
有异动。

至于是否可以暂时隐藏、显示 GUI Request window (即 Map, Unmap), 则视该 window
的定义而定,并非所有的 window 都需要此功能。例如 menusel 就没有。

综上所述,如果输入法模组决定要开启两个 GUI Request 视窗 (假设都是 menusel),
则程式这样写即可:

greq_t *new_gui_request(int type)
{
    static int reqid=0
    greq_t *greq;

    greq = malloc(sizeof(greq_t));
    greq->reqid = (++reqid);
    greq->type  = type
    greq->deleted_return = (ubyte_t)0;
    greq->next = NULL;
    switch (type) {
    case GREQ_MENUSEL:
	greq->data = (void *)malloc(sizeof(greq_menusel_t);
	.......
	break;
    }
    return greq;
}

inpinfo->gui_request = new_gui_request(GREQ_MENUSEL);
inpinfo->gui_request->next = new_gui_request(GREQ_MENUSEL);
/* 这里就是开两个 GUI Request, 用 link list 串起来。若要关掉任何一个,将它
   从 list 里拿掉即可 */


3. Menu Selection Window:

menusel 所需的资料结构如下:

typedef struct {
    unsigned short n_item;              /* number of item lists. */
    unsigned short head_item;           /* head index of the item lists. */
    unsigned short n_sel;               /* number of selection keys. */
    unsigned short n_sel_return;        /* num of selection returned by xcin. */
    char *selkeys;                      /* the selection keys. */
    unsigned short focus_item;          /* index of the focused item. */
    unsigned short focus_elem;          /* index of the focused element. */
    ubyte_t enable_focus_elem;          /* use focus element facility or not. */
    menu_item_t *item;                  /* the item lists. */
} greq_menusel_t;

Menusel 视窗分两部分,左半边称为 item head, 右半边称为 item list, 如下图:

	head1  |  item1-1  item1-2  item1-3 ....
	head2  |  item2-1  item2-2  item2-3 ....
	.....  |  .......  .......  .......

该视窗的大小是固定的,长度等于 xcin 主视窗的长度,高度最多是五列,如果
n_item 的数目小于五的话,则列数就会等于 n_item 的值。 n_item 可以超过 5,
而视窗显示时是从 head_item 开始作第一列显示。例如,您有一笔资料总共有 8
个 item list, 但 head_item 是设成 2, 则显示如下:

   (item head)   (item elements)
	head2  |  item2-1  item2-2  item2-3 ....
	head3  |  item3-1  item3-2  item3-3 ....
	head4  |  item4-1  item4-2  item4-3 ....
	head5  |  item5-1  item5-2  item5-3 ....
	head6  |  item6-1  item6-2  item6-3 ....

视窗本身不会去拦捷处理任何输入字键,因此,如果您要实作 menu 上下卷页的功
能,您可以在 module function keystroke() 接到上键时,将 head_item 减 1,
接到下键时将它加 1 即可。

n_sel 是指在一个 item list 中最多有几个选择项,但它只有在 selkeys 有设定
时才有用, selkeys 是设定显示出的选择键,而 n_sel 就是 selkeys 的数目。
例如,我可以设定:

	greq_menusel->selkeys = "asdfghjkl;";
	greq_menusel->n_sel = strlen(selkeys);

如此显示出来的 item list 就是:

	headN  |  a itemN-a  s itemN-s  d itemN-d  ....

注意每个 item 前面都会伴随一个 selkey, 就是您在 greq_menusel->selkeys 中
所设的内容。当然您在这里可以设成 NULL, 则预设就直接以 "1234567890" 来使用。
然华A menusel 的宽度不见得足以容纳所有的 item element, 如视窗画不下时,xcin
会透过 n_sel_return 回报说实际上只画了几个 element,故输入法模组可以藉此做
出必要的调整。

focus_item 是指那一个 item 要特别以反白标示,就好像游标停在那一个项目上一
样。在这里反白会标示在该 item list 的 head 上,图示如下 (标示在第二个 item):

	head1  |  item1-1  item1-2  item1-3  ....
       <head2> |  item2-1  item2-2  item2-3  ....
	.....  |  .......  .......  .......

如果将 enable_focus_elem 设为 1 时,则反白标示也可以标示在一个 item list 中
的某个 element 上 (由 focus_elem 来指定),例如 (标示在第二个 item 的第三个 
element):

	head1  |  item1-1  item1-2  item1-3  ....
       <head2> |  item2-1  item2-2 <item2-3> ....
	.....  |  .......  .......  .......

若将 enable_focus_elem 设为 0, 则 item list 中不会有 element 的反白标示,且
focus_elem 也不会有作用。

最后的 menu_item_t *item 是指向每一个 item list 的阵列,如:

	item[0].title     ==> head1
	       .elements  ==> item1-1 item1-2 item1-3 ....
	       .........
	item[1].title     ==> head2
	       .elements  ==> item2-1 item2-2 item2-3 ....
	       .........


4. Elements in each items:

每个 item list 的细部定义如下:

typedef struct {
    wch_t *title;                       /* title of the item list. */
    wch_t *elements;                    /* elements of the item list. */
    ubyte_t *elem_group;                /* grouping info. of the elements. */
    unsigned short n_elem;              /* size of "elements" array. */
    unsigned short head_idx;            /* head index of the item list. */
} menu_item_t;

其中 title 就是上面那几个图中某一 item 的 head, 而它的 element list 则是
设在 wch_t *elements 中, n_elem 是 elements 阵列的个数。如果 elem_group
是 NULL, 则每个 elements 的阵列原素就是一个选项,如果 elem_group 不是 NULL,
则可以用它来指定多个 elements 阵列原素合成一个选项,例如:

	elem_group = NULL		/* 每个 element 原素都是各别选项 */
	title = head1
	elements = {A}, {B}, {C}, {D}
	-------------------------------------------------------------------
	head1  |  1.{A}  2.{B}  3.{C}  4.{D}

	elem_group = ....		/* 可以指定若干 element 原素合成一 */
	title = head2			/* 个选项			   */
	elements = {A}, {B}, {C}, {D}
	-------------------------------------------------------------------
	head2  |  1.{AB}  2.{C}  3.{D}

至于 elem_group 的用法,与 inpinfo 中 lcch_grouping 与 mcch_grouping 的用
法一模一样,您可以参考 xcin-2.5/doc/internal/module 有关这两个东东的使用
说明,即可了解。

最后一个 head_idx 它的作用与先前提的 gui_menusel_t->head_item 一样,也是
告诉 xcin 说此 item list 要从那一个 element 开始画起。

PS. 写到这里,我突然想到, greq_menusel_t 里头的 n_sel_return 其实应该要
    放在 menu_item_t 里头,这样才合理,因为每一个 item lists 的长度可能不
    见得一样 (就算它们的 element 数都相同),有可能 item A 画到第十个 element
    都还没超过长度,但 item B 画到第八个就超过了,因此必须每个 item 都回
    报一个 n_sel_return。这部分我再来改。

最后提一点: 上述所提的所有的 index, 如 head_item, focus_item, focus_elem,
head_idx 等,全部都是由 1 开始计数,而不是从 0 开始,请不要弄错了。


5. Example:

底下这段 example code, 就是我在那个 screen shot 中的 menusel window 。这段
code 写得很丑,并没有放在 xcin-2.5.2-pre1 里头,只是纯测试用的,各位可以参
考。

static greq_menusel_t *data;
static menu_item_t mitem[3];
static wch_t mtitle1[10], element1[20];
static wch_t mtitle2[10], element2[20];
static wch_t mtitle3[10], element3[20];
static ubyte_t elem_group[10];

/* initialize gui_request */
inpinfo->gui_request = malloc(sizeof(greq_t));
inpinfo->gui_request->reqid = 1;
inpinfo->gui_request->type = GREQ_MENUSEL;
inpinfo->gui_request->deleted_return = (ubyte_t)0;
inpinfo->gui_request->next = NULL;
data = malloc(sizeof(greq_menusel_t));
inpinfo->gui_request->data = (void *)data;

/* initialize menusel */
data->n_item = 3;
data->head_item = 1;
data->n_sel = 10;
data->n_sel_return = 10;
data->selkeys = NULL;
data->focus_item = 1;
data->focus_elem = 1;
data->enable_focus_elem = (ubyte_t)1;
data->item = mitem;

/* initialize item 1 */
mitem[0].title = mtitle1;
	big5str_to_wch(mtitle1, "台湾大学");
        mitem[0].elements = element1;
        mitem[0].elem_group = NULL;
        mitem[0].n_elem = 4;
        mitem[0].head_idx = 1;
	big5str_to_wch(element1, "现在时间");

/* initialize item 2 */
mitem[1].title = mtitle2;
	big5str_to_wch(mtitle2, "物理系");
        mitem[1].elements = element2;
        mitem[1].elem_group = NULL;
        mitem[1].n_elem = 8;
        mitem[1].head_idx = 1;
	big5str_to_wch(element2, "我是居士正在测试");

/* initialize item 3 */
mitem[2].title = mtitle3;
	big5str_to_wch(mtitle3, "谢东翰");
        mitem[2].elements = element2;
        mitem[2].elem_group = elem_group;
        mitem[2].n_elem = 8;
        mitem[2].head_idx = 1;
        mitem[2].elem_group[0] = (ubyte_t)4;
        mitem[2].elem_group[1] = (ubyte_t)2;
        mitem[2].elem_group[2] = (ubyte_t)2;
        mitem[2].elem_group[3] = (ubyte_t)2;
        mitem[2].elem_group[4] = (ubyte_t)4;


6. 结语:

看到了这,您是不是已头昏脑胀了呢?我是已经写得头昏脑胀了 :-))
我想我大概可以很自豪地表示, xcin 可以算是自由软体界中写得最 ugly 的 XIM
server 了 ....

我在上述提到的若干有待改进的问题,以及上一篇 xcin & OverTheSpot 中提到
的问题,我会尽快做出 patch release 出来。



T.H.Hsieh

----- End forwarded message -----


-- 
[ This mail was originally sent to  debian-chinese@lists.debian.org ]
[ and was forwarded to this list automatically. Big5 characters are ]
[ also converted to GB at the same time, Please note that there may ]
[ be errors during the conversion as this is not done by a human!   ]


Reply to: