X windows系统使用指南(十五)
时间:2007-02-17 来源:PHP爱好者
第17章 定制你的键盘和滑鼠 -- 转译
电脑的键盘通常含有一些 "特殊功能" (special function)键,在此有一
些方法来 "制定"(program)这些特殊功能键,使它们能完成特定的功能以适合
你工作的方式。例如,你可以定义一些键来输入那些你常用的命令,或只需按
一个键便能够输入一些程式的片段。
在X中,你能制定的不只是功能键而已,其它一般的键和滑鼠的按钮也都
可制定。对每一个应用程式,你均可指定特别的功能给键盘和滑鼠按钮,或两
者之组合。 (例如在 xedit中你可以结合SHIFT 键和滑鼠的右按钮来让你向前
移动一个单字)。 所有使用X Toolkit的程式均允许使用者利用一个被称之为
键盘转译 (translations) 的设施来执行此种定义,且此种定义藉着正规的
resources 结构传递给应用程式。 (那些不使用X Toolkit的应用程式,同样
地也可以用相同的设施来制定, 但它们需个别的定义所以不能广泛地应用,从
现在起,我们假设每当讨论有关转译的种种,均为对那些使用X Toolkit的应
用程式而言。)
就如同所有的 resources一样,转译是当应用程式执行时才被处置。例如
你可以拥有数个具备不同转译设定的 xedit,在同时一起执行。( 你可以让一
个xedit 适合编辑本文,另一个适合编辑程式码,而另一个适合编辑文书)。
本章讨论转译 -- 包刮它们的定义格式,如何将它们设定到应用程式,和
它们所涵盖功能的范围。我们首先以实例来介绍,逐渐地导引你看到不同的角
度。而後比较正式和详细地讨论转译。最後,我们列出当你使用转译时常会碰
到的错误,并给你一些如何克服这些问题的提示。
17.1 实际使用转译
Toolkit 转译结构最简单的用途便是让你制定你键盘的键。例如,当你使
用xterm 为一个执行一般shell 命令的视窗时,你可能希 定义一些特殊功能
键来输入你常用的命令,且希 指定的关系如下:
当我按下这个键时... 我希 这个字串被输入
F1 rm core *.tmp
利用Toolkit 达到此目的方法为:指定一个值给使用转译的widget中的resources
。此值设定应用程式中所必需的定制 (customisation),且被Toolkit 的 Trans-
lation Manager (转译管理器) 所处理。此 resources属於类别Translation,
且其成员名称几 一定是translations。
在下一节我们将看到设定到resources 中的值的格式。
17.1.1 如何对一个应用程式指定转译
对前述xterm 的例子,我们定义 (在即将被应用程式读入的resource资料
库中或一些resource档案中) 一个规格类似:
xterm*VT100*Translations: (contd.)
F1: string("rm core *.tmp") 注意:不完整!!
其意为在任何类别 VT100的 xterm widget 中,当键 F1 被按下时, 入
字串 "rm core *.tmp")。
不幸的是,并没有这麽简单,转译管理器会把上面的规格解释为 "去掉所
有现存的转译,且加入... " ,所以所有正常的像 " A键是 入一个 A" 这种
系结 (binding)都会消失。为了克服这点,你必需使用一些被称为 directive
(指引)的语法 入在 resource 值之前:
xterm*VT100*Translations: #override(contd.)
F1: string("rm core *.tmp")
通常你会希 保持大部份现存的系结,而只是把你明确指定的值覆盖上去
,所以你一般都是在你的转译表中,指定 #override。
现在这个规格可以开始工作了,藉着启始一个 xterm且把此规格 (在两个
单引号 ()中间的部份) 当成选项 -xrm 的引数来测试它:
xterm -xrm xterm*VT100*Translations: ... 等等
按下特殊功能键 F1,你将看到指定的字串成功的 入,但并未包含新列字元
(newline) ,你可以用一点语法的技巧来克服它,像:
xterm*VT100*Translations: #override(contd.)
F1: string("rm core *.tmp")string(0xd)
这解释了以下两点:
1. string()的作用和它的引数相关。你可以直接输入本文 (例如 string(lpq))
,但如果本文包含空白或非字母字元,则必需在本文前後加上双引号。
如果引数是以 "0X" 开头,则将其後解释为十六进位,并 入相对的
ASCII 字元。(例如,0xd是RETURN)
2. 在你指定此功能时可结合一个以上的作用,在上例,我们用到 string()
作用两次,如果我们知道其它的作用,我们也一样可以系结起来。
定义许多的转译在一起
你可以根据需求在一个表中定义许多的转译。假设,我们在前面的转译中增加
系结:
当我按下这个键时... 我希 这个字串被输入
F2 lpq-Plpa3
对此的转译为:
F2: string("lpq-Plpa3")string(0xd)
所以可以将本列加入前面的表中。但是转译管理器的格式规则告诉我们必需将
两个转译以 "n" 分开且独立成为一列:
xterm*VT100*Translations: #override(contd.)
F1: string("rm core *.tmp")sting(0xd) n(cond.)
F2: string("lpq-Plpa3")string(0xd)
以上的形式将造成管理上的困难,你可以藉着包含 "隐藏的新列字元" 来使它
具可读性一些: (新列字元以倒斜线 ""处理)
xterm*VT100*Translations: #overriden
F1: string("rm core *.tmp")sting(0xd)n
F2: string("lpq-Plpa3")string(0xd)
你可以放置任意多个你所需的 "隐藏的新列字元" , 且几 在任何地方均可,
它们只是被忽略而已。 (只要和转译管理器相关,甚至你每隔一个单字便使用
一个也没关系。但千万不要在一个规格的 resource 特徵部份使用它们,它们
无法被resource manager解释,也没有相同的效果。) 如果你感觉有些混淆,
不用担心。简单地说,resource结构需要的是要在一列中的一个resource规格
的 "值" 的部份,而转译管理器以分开的列来分开 (也就是以n终结),而使用
者刚好以每一个实际分开的列代表一个意义以增加可读性,所以规则很简单:
在除了最後一列的每一个转译列均加上一个 "n"。
17.1.2 转译可系结许多型式的作用
上述的 xterm范例,展示了如何能够当你按下一个键时, 入任意的字串。但
转译结构的功能比这更多 -- 它可以系结任何widget所提供的作用到按键,让
我们详细一点地看一下这些作用。
前述的例子,我们在 xterm的 VT100 widget完成了键F1和F2在 string()
上的对应。我们将仍以 xterm为例,说明更多的作用。
查阅xterm 的指南页,在标题KEY TRANSLATIONS 和 KEY/BUTTON BINDINGS
你将发现列有数个作用。我们将定义一个转译对应键F3到insert-selection()
作用之上,所以我们可以用键盘来取代滑鼠,将先前 "剪"下的本文 "贴"出。
指南页告诉我们此作用需要一个引数,从列出的预设系结,我们可以看出预设
的 "剪贴" 结构为使用 CUT_BUFFER0, 所以我们将CUT_BUFFER0当作引数。我们
的resource规格是:
xterm*VT100*Translations: #overriden
F3: insert-selection(CUT_BUFFER0)
到目前为止,这只是一点小小的便利。然而,假定说你花了许多时间在本文文
件上工作,你用 tbl格式化,你用 nroff在萤幕上预视 (preview)它们,用
troff 排版,且将输出送到你的一个用过滤器 (filter) 为tr2printer的印表
机上。设定转译为:
xterm*VT100*Translations: #overriden
F3: string("ed") insert-selection(CUT_BUFFER0)
string(0xd)n
F4: string("tbl") insert-selection(CUT_BUFFER0)
string("| nroff -man") string(0xd)n
F5: string("tbl") insert-selection(CUT_BUFFER0)
string("| troff -man -t | tr2printer") string(0dx)
xterm 会确定这些转译是以xrdb自资料库载入或是在一个resource档案中,并
加以处理。现在当你启始xterm,用滑鼠 "剪"取你所需的工作的档案名称。接
下来,便可按F3键编辑它,按F4键预视它,和按F5键在硬拷贝上排版它。
更多的widget作用范例 -- xbiff
查阅xbiff 的指南页:在ACTIONS 的标题下,你将看到Mailbox widget所支援
作用的名单。它惟一预设的转译为当你按下任何按钮时降下信件 (mail)的旗帜
(flag)(作用unset())。我们将设定转译让你以键盘来运用这些作用,将这些作
用对应到 "?" 和 "UP" "DOWN"两个方向键如下:
? check() 有新的信件吗?
UP set() 升起信件的旗帜
DOWN unset() 降下信件的旗帜
以下是相关的转译表:
xbiff*Mailbox*Translations: #overriden
?: check()n
Down: unset()n
Up: set()
以此测试之:用xrdb从你的resource资料库载入这些设定,然後启动xbiff ,
将指标移到视窗内。重复地按下Up和Down游标控制键以升起和降下信件旗
帜。
找出有哪些作用被提供
你对widget作用将和widget名称遭遇相同的问题:如何找出某个widget到底提
供哪些作用以及它们能做些什麽?同样地,没有一个完美的解答,但有一个合
理的方法来处理:
1. 查看应用程式的指南页。大多数的应用程式有它们自己专门的作用文件
。例如:xbiff 有一节叫做ACTION,而xterm 有两节关於转译和作用的
文件 -- KEY TRANSLATIONS 和 KEY/BUTTON BINDING。
2. 最初的指南页可能给你提示,或甚至直接告诉你它用到何种widget的类
别,所以你可以查看它的widget set文件中的特定的widget。(在 core
版中惟一的widget set为Athena,所以你在此不易出错)。即使指南页未
告诉你widget的类别,当你对系统熟悉之後,你将对一个widget是否为
标准型态较具有概念,如果还是不行 ...
3. 查看程式的原始码,看看用到什麽widget的类别,以及widget提供了哪
些作用。
17.1.3 转译系结作用到一序列事件,不只是单一键
我们已经看到转译让你设定 入,转译结构也能让你系结这些作用:它可以是
单一的键,或是一序列的键,或者是事实上一序列任何的X事件。
让我们继续以xbiff 为例,看看如何转译一序列的键盘字元。例如我们定
义字元字串的转译如下:
look check()
raise set()
lower unset()
以下为相关的转译表:
xbiff*Mailbox*Translation: #overriden
l,o,o,k: check()n
r,a,i,s,e: set()
l,o,w,e,r: unset()
以此测试之 -- 载入设定和启动xbiff ,将指标移到视窗内。现在你可藉
着输入完整的字串来升起和降下旗帜。例如键入五个字元 r, a, i, s, e以升
起旗帜。对xbiff 的两个表有几点值得说明:
. 键的名称可以用不同的方式指定。正常的印出字元直接指定 (如"w")
, 其它的字元则拼出全名(如"Down").附录A "文件指引" 告诉你
在何处可查到键的名称,但在本节下面有更简单的方法。
. 对字元字串,你必需一一指定,并以逗点分开
(如"l,o,o,k").
. 转译可允许相同开头的键,例如 "look" 和 "lower" 均拥有相同的开头
"lo",对转译管理器不会形成问题。
找出键的名称
找出转译所需的键的名称,最简单的方法为执行xev ,将指标移到视窗内,按
下你所需的键,则键的名称会出现在括弧内字串keysym和一个十六进位数之後
。例如在xev 的视窗内按下游标控制键DOWN,它的输出如图17 - 1,在其中你
会看到
(keysym 0xff54, Down)
也就是说,键的名称为Down。
┏----------------┓
│ P209. fig 17 - 1 │
│ │
│ │
│ │
│ 图 17 - 1 xev 显示键名称的输出 │
┗----------------┛
你可以在转译中使用任何型态的事件
到目前为止,我们所写的转译都是系结作用到一个按下的键盘字元。但我们曾
说过,转译结构可系结作用到任何事件,而不只於按下键盘而已。可能的事件
型态非常的多,在此我们只提及一小部份:
型态 意义
按下一个键
按下一个键 (只是另一个名称)
放松一个键
按下一个滑鼠按钮
放松一个滑鼠按钮
指标进入视窗内
指标移出视窗外
我们已经使用过按下一个键的事件,让我们系结xbiff 作用到滑鼠按钮以取代之:
xbiff*Mailbox*Translations: #overriden
Button1: unset()n
Button2: check()n
Button3: set()
你可以看到语法和前面相似:你先给定一般性的事件型态 (例如 或
),其後跟着你所需事件的事件细节部份 (例如s 和Button3) (
Button 1, 2, 3分别对应到左、中、右按钮)
对一序列的事件的转译
就如同我们定义了一序列按下键事件的转译(set,unset和check),我们当然
也可以定义一序列的滑鼠事件。事实上你转译的一序列的事件可以任意组合在
一起,你可以在一个转译的左边随意混合事件的型态。所以你可以定义如下的
转译表:
xbiff*Mailbox*Translations: #overriden
Button1, ?, Button3: check()n
Button1: u, Button3: unset()n
Button1: s, Button3: set()
也就是说,用到check(),你必需依序先按下按钮1 (左按钮),然後按下"?"
键 ,最後按下按钮3 (右按钮)。 这个范例并不是很好,但对於一些危险或不
可取消 (irreversible) 的作用 (例如删除一个档案,或是覆写一个缓冲区的
内容) ,你可以依照这种方式来使用转译。你需要使用一个非常谨慎的命令序
列,才能用到此作用,这样使得使用者不可能因意外而输入此命令。
使用非键盘和非滑鼠事件的转译
通常你是对按下或放松滑鼠按钮或键盘的键定义转译。但我们曾经说过,你可
以对任何事件设定转译,例如指标移入或移出一个widget的视窗。让我们以
xman的主选项视窗 (图10 - 3) 为范例来解释它。这是一个相当人为的范例,
因为它没有任何用途。但无论如何,它很容易被看出在做些什麽操作。
查看xman的指南页,在X DEFAULTS标题下,你将看到概括的xman所用到的widget
的名称和类别:主选择项视窗widget的名称叫topBox,类别名为Command。这
是一个好的猜测,因为在选单操作盒的方法。我们可用第15章所提过的技巧
来确认它,使用以下的命令:
xman -xrm "*Command*backgroundPixmap: scales"
且所有的Command widget将会有鱼鳞状的背景)。
这和我们先前的范例有一个重要的不同:我们所用到的作用不是由特定的应用
程式指定,而是由标准的widget提供 (本例中为Command widget,在 "X
Toolkit Athena Widget"使用手册中有描述)。
在我们定义任何东西之前,先来看一看此widget预设的功用,以便我们能
够了解有些什麽事发生和有哪些widget的作用会做。启动xman,移动指标进入
Help 盒,你会看到盒的外框变成高亮度 -- 这是highlight() 在作用。将指
标移出,盒的外框恢复正常 -- 这是unhighlight() 作用。将指标再度移入
Help 盒,按下一个滑鼠按钮,保持按住不放。则盒内的色彩反转 (盒内的文
字变成预设的背景色,而原来视窗的背景变成视窗的前景色)。-- 这是set()
在作用。继续保持按住滑鼠按钮,将指标移出视窗外,盒内色彩恢复正常 --
这是reset() 在作用。一个正常 "碰触一下" (clicking on) Help盒的次序为:
1. 移动指标进入盒中:highlight()将外框变为高亮度。
2. 按下按钮:set() 反转盒中的色彩。
3. 松开按钮:notify()开始作用,造成程式建立求助视窗(help window)
。在进行中时,盒的色彩保持反相。当视窗建立完成之後,reset()反
转盒内的色彩为正常,但外框仍保持高亮度。
4. 将指标移出视窗:unhighlight()将外框恢复正常。
现在你了解了有哪些作用,我们将定义一些转译来改变原先进出视窗的作
用:
*Command*translations: #overriden
: reset()n
: set()
用这个奇怪的转译表,当你一开始移动指标进入盒中,什麽事也不会发生,但
当你移出指标时,色彩会反转。如果你再度移动指标进入盒中,色彩会变回正
常。其它的作用和前述相同。
使用修饰键来修饰事件规格
有时你指定的转译希 能同时按下一或多个修饰键 (modifiers) ,例如你要系
结一个作用到和META键同时按下的一个键,或是当CTRL和SHIFT 同时按下的滑
鼠按钮。到目前为止我们还没有任何办法可指定如此。我们不能用事件序列达
成这点,因为它是依序定义的,而我们需要的是指定同时,例如 "按下X 键且
CTRL键同时被按下"。
欲在转译中指定修饰键,你只需在事件名称之前加上你所需的修饰键名。
例如在xterm 中,定义meta-i为 "贴" 上一次 "剪" 的本文,使用:
*VT100*Translations: #override
Meta i: insert-selection(PRIMARY, CUT_BUFFER0)
因为这种修饰键/事件型态的组合十分常见,转译管理器允许使用一种缩写的
形式。相等於上面第二列的写法为:
i: insert-selection(PRIMARY, CUT_BUFFER0)
我们可以对滑鼠事件做同样的处理。让我们对xedit 定义转译,使得使用
滑鼠可以在本文上方便地移动,我们首先的尝试如下:
*Text*Translation: #override
Shift : forward-character()n
Shift : forward-word()n
Shift : next-line()n
Ctrl : backward-character()n
Ctrl : backward-word()n
Ctrl : previous-line()
如果你测试它,奇怪的现象会发生 -- 游标好像会自行其是,而且本文的片段
会一下子被选择,一下子又取消选择。发生这种现象的原因是Text widget 的
预设系结仍然会作用,它包含的转译像:
: extend-end(PRIMARY, CUT_BUFFER0)
你可能认为这不会影响你,因为当你松开按钮时你总是按着SHIFT 键或CTRL键
。但事实上会作用:转译管理器对於你未定义的修饰键解释为你不在 它们的
影响,所以松开Button1 时会对应到上述的规格。为了克服这点,我们对那些
可能不小心便会发生的按钮松开事件定义转译,并系结到一个空 (null) 作用
。这些转译当被对应到时会盖掉预设的转译。对使用Text widget 我们需再增
加两列,才是一个完整的转译表:
*Text*Translation: #override
Shift : forward-character()n
Shift : forward-word()n
Shift : next-line()n
Ctrl : backward-character()n
Ctrl : backward-word()n
Ctrl : previous-line()n
Shift : do-nothing()n
Ctrl : do-nothing()
这解释了下列几点:
·我们对滑鼠事件使用了缩写的语法,也就是先前的语法像Button1
以取代。转译管理器容许一些缩写的语法存在。 (我们在前
面看到的 也是一例)。
·我们用 do-nothing() 当作一个哑 (dummy)作用,就好像它是列在Text
widget的文件中一般。事实上这个作用是不存在的,因此会导致错误的
讯息出现,但因为我们本来就是要用它来什麽事也不做的,所以无需介
意。
·对於我们方才指定的哑作用,我们用了一个事件 便代表了三个
按钮。相同地,转译管理器把从缺的修饰规格的解释为 "对任何",在一
个事件中缺少细节部份 (例如在规格"Button1"中"Button1"的部
份) 解释为 "对任何所有的细节部份"。
这点在转译中有一个非常常用的形式为:
: ...
因为缺少细节部份,所以可被用於所有按下键 (key-press)事件,也就
是对所有的键。事实上在Text widget 上有一个预设的转译为:
: insert-char()
insert-char()作用的功能为当一个键被按下时, 入相对应的ASCII字
元。
17.1.4 复合的转译表及范例
到目前为止,我们把所有的转译均应用於整体的widget类别。但你能对个别的
widget指定转译,就如同resource一般。在此我们将对xman定义更多的转译。
我们将对Help盒widget (对应作用到助忆(mnemonic)字元)只用到键盘事件,对
Quit盒只用到视窗事件。为了达到此点,我们将对转译应用到的widget 给予明
确的名称。我们的转译表如下:
*Help*translations:
h: highlight()n
u: unhighlight()n
n: notify()n
s: set()n
r: reset()n
LineFeed: set() notify()
Quit*translations: #overriden
: reset()n
: set()
有几点特别的语法需要注意:
·在此我们对相同类别中不同的widget指定不同的转译,所以我们需要知
道成员 (instance) 名称。不幸的是,这些成员名称 (Help,Quit,
Manual Page)并不明显。如果它们在文件中找不到 (本例即找不到),那
你只能用猜的或是去查看原始程式了。
·对於Help,我们省略了常用的#override,因为我们对此widget不需要考
虑任何预设的系结。特别的是,当指标进入视窗时,我们不要此widget
呈现高亮度,如此我们才能看出这个转译的效用。
·由於省略 #override,我们将这个转译规格移至第一列。(如果不这麽作
,而且对第一列仍以n 作结束,我们将得到错误:
X Toolkit Warning: translation table syntax er-
ror: Missing : after event sequence.
X Toolkit Warning: ... found while parsing
因为n是用来区隔转译规格或类似像 #override 指令的)。而将此列和
第一个规格以隐藏的新列字元区隔,就如同:
*Help*translations:
h: highlight()n
...
·对LineFeed那一列的转译,包含了复合的作用,和前面xterm 中复合的
string()作用类似。
我们已大致涵盖了你所常用的转译。基本的概念很简单,生成它们的结构
也不复杂,但它们非常的令人迷惑。原因是它是一个发展中的糸统,随着发行
版本所附的文件并不是很多。下一节重复本节所述的,本节均以例子来介绍观
念,下一节我们将对转译的规则,作比较正式的描述。
17.2 转译 -- 格式和规则
转译是一个由Toolkit 提供的一般性结构,它让使用者指定当某些特定的事件
由widget接收到时,一个widget应完成何种作用。Toolkit 中处理转译的部份
被称之为转译管理器。
转译由widget指定,它的确是一个widget的每一个成员。一个转译的集合
称之为一个转译表,而这个表藉着标准的resource结构传递给应用程式。widget
(对转译而言意味深长) 会有一个Translation 类别的resource属性,通常的
成员名称为translation。这个转译resource期待的一个值即为一个转译表。就
像所有其它的resource一般,你可以在同一个应用程式对不同的widget指定不同
的resource,而且你能以类别名称或成员名称或二者混合来指定它们。
每一个widget定义了它所提供的作用,不论是在数量或型态上,它们都是
极富变化的。
转译可被各种不同型态的事件指定,不仅只於键盘和滑鼠事件而已。任何
序列的事件均能被处理,就如同单一事件一般。
转译和转译表在 " X Toolkit Intrinsics " 使用手册附录B一节中,有
简洁地描述。它不是初学者查看转译的好地方,但它含有完整的事件型态、修
饰名称等等的表列,在此不再赘述。
17.2.1 转译表的格式
一个转译表大体上的格式如下:
[optional-directiven] list-of-translations
每一个 list-of-translations 由一或多个转译组成,格式如下:
event-sequence : list-of-actions
当event-sequence发生时,规格中的list-of-actions 会由widget所完成。如
果在一个表中,有多於一个的转译,每一个需以 "n" 区隔开。
我们首先来看一下选项的指令,然後看一下list-of-translations的细节
部份。
17.2.2 转译指引 -- #override 等等
选项指引 (directive) 告诉转译管理器,它应对任何已设定之相关widget在此
转译集合中应如何处理。
#replace : 清除所有现存的对应,只采用在转译表中所含有的。(只使用新
的)。
#override : 强制留下现有的对应,加入转译表中。如果在表中有任何项
目设定,旧有的即被覆写。也就是说,旧有的被新有的取代。 (结合
旧有的和新的,但新的比较重要)。
#augment : 强制留下现有的对应,加入转译表中。如果在表中有任何项
目设定在现有的设定存在,使用旧的而忽略新的。 (结合旧有的和新的
,但旧的比较重要)。
如果未设定指引,预设为 #replace 。
17.2.3 个别的转译规格格式
每一个转译的格式为:
event-sequence : list-of-actions
让我们来看一看此规格的两个部份。
事件和事件序列(event-sequence)的格式
一个事件序列包含一或多个事件规格(event-specs), 其格式为:
[modifiers] [repeat-count] [detail]
除了事件型态(event type)外,均为可选择。(<>中为必需)。
modifiers : 这是基本设计中比较精巧的部份,我们在下一段说明。
event-type : 指定我们有兴趣的事件的型态,例如按键()、
松开按钮()或指标离开视窗()等等。
detail : 指定我们有兴趣的特定型态。如果你省略细节栏(detail field
),事件规格将对应到任何detail,如此,将对应到所有的按键
事件。此格式指定到每一个事件型态。对指定事件型态的细节栏为:
·对、和事件,细节如果不是键的名称(例如
"s"),便是 keysym (keysym是按键以开头为"0x"的十六进位
数表示,将於下一章详细解释)。
·对於按钮事件,细节就是按钮的名称,也就是 Button1 ... Button5
中的一个。例如我们先前使用过的"Button1"。
型态/细节的缩写:常用於转译管理器的一些事件型态和细节的组合,允许
你对它们使用缩写:
缩写 相等的全名
Button1
...
Button5
Button1
...
Button5
repeat-count : 这指定了事件需要的次数。如果被指定,它们被包含在
括弧之中。例如:
(2)
指定需对一号按钮(button-1)碰触两次。如果你在後面再加上加号(+)
,其意为碰触的数目需大於或等於指定。例如:
(3+)
意为需碰触三或更多次。预设的重复次数为一次。
一个事件序列以一或多个事件规格组成,以逗点分开。当这个事件的序列
在其widget发生时,相关的作用便会运作。
当序列发生时,转译管理器会根据一些规则决定它自己是否被满足。我们
用一个例子以便仔细地观察,假设你对两个字元序列set 和unset定义了转译:
·概略地说,如果个别的事件依序发生,转译管理器会被满足,其它的事
件 (那些你未指定的事件) 如果在指定的序列中间发生,不会妨碍序列
被满足。例如,set 可被sweat 和serpent 对应。
·如果介於其间的未指定事件,启动了转译表中的另一个事件序列,转译
管理器会放弃原先的序列,而尝试着去满足新的序列。例如,set 不会
被sauerkraut对应,因为u 会使得转译管理器对应到unset。
·如果在一个事件的集合中有超过一个的事件序列发生,转译管理器只会
应用到一个转译:
- 如果一个序列对应到结束 (右端),较短的那个序列只有在不包含於
较长的序列才会发生。所以如果unset 被对应到,对set 转译将不会
作用。
- 如果一个序列是在另一个序列的中间发生,例如,如果你定义序列
at和rate,则较长的那个永远不会被对应到。
事件修饰键
修饰键 (modifiers)是一些键或按钮,系指当主要事件发生时,那些必需被按
下才会让转译管理器满足的键或按钮。你可以对键、按钮、移动、进出视窗等
事件指定修饰键。常见的修饰键为:
Button1 ... Button5
Ctrl Shift Meta
Lock
如果你未指定任何的修饰键,转译管理器会解释为: "当事件发生时,不
论修饰键是否被按下,均会被接受"。例如,会被满足,不论当时
SHIFT 或META键是否有被按下。
如果你真的需要指定 "只有在没有修饰键被按下时才接受此事件"。则需使
用虚拟修饰键 (pseudo-modifier) None。例如,None 会使得当按
钮按下时若META键也被按下则不会满足。
对一个事件指定一些修饰键意为 "只要符合转译中指定的修饰键,其它的
修饰键不需介意"。它并没有 "一定要完全恰好符合才可以"的意思。例如,
Ctrl a 在你按下 meta-ctl-shift-a 时仍会被满足。
如果你真的要指定 "只有刚好符合修饰键的才要",在修饰键之前加一个惊
叹号 (!)。例如,!Ctrl a 在你按下 meta-ctl-shift-a 时不会被满足。
对一个修饰键的集合 (可能是空集合) 作限制,意为 "除了这些修饰键不
不接受",需要在不接受的修饰键之前加一个(~)号。例如,Shift~Meta t
会被ctl-shift-t满足,不会被meta-shift-t满足。
键事件通常忽略大小写,如果你要区分,需在之前加一个冒号(:)。例如,
不论 H或 h均可符合H,但只有H 才符合 :H。
就如同对常用的事件型态/细节配对有缩写一般,转译管理器对常用的修
饰键/事件型态配对同样地提供缩写:
缩写 相等的全名
Ctrl
Shift
Meta
Button1
...
Button5
任何按钮的
作用的格式和作用的表列
每一个转译在一或多个作用之上系结一个序列的一或多个事件。在表列中的个
别作用是以空白分开的。 (不可用逗点分开,那将会导致错误)。
个别的作用格式如下:
action-name(parameters)
即使没有叁数被指定,在作用名称 (action-name)後的括弧,仍然不可省略。
例如:
start-selection()
如果在作用名称和左括弧中间留有空白,你将会得到一个错误。
作用名称只包含了字母、数字、钱号($)、底线(_)四种字元。每一个widget
提供它自己的作用集合 (如果有的话) ,且自我包含这些作用名称的硬码
(hard-coded)表列。
叁数(parameters)是一个零到多个字元字串的表列,中间以逗点分开。叁
数的意义为对特定的作用作指定 (事实上大多数的作用并没有任何叁数) 。叁
数字串可以不加引号,例如:
insert-selection(PRIMARY)
或者前後加上双引号,这种情形通常为叁数字串内包含了空白或一个逗点,例
如:
string("plot")
没有一个一般性的方法,让你在叁数字串中的任何位置包含一个双引号,虽然
像这样 string(ab"cd)将双引号放在字串中间是可被处理的。也没有一般性的
方法在同一个叁数字串中同时包含字串和双引号。因为如此,有些widget在解
释它们自己的叁数时,可以自行加入它们自己的语法规则。例如:对xterm 的
VT100 widget的 string()作用,如果一个不带双引号且开头为 "0x"的字串,
此字串被解释为代表一个ASCII 字元的十六进位数。
在此结束我们对转译规格及格式的描述。由此,你应有能力了解在不同X
手册列出的转译,且可写你自己的转译。为了帮助你,下节列出你常见的问题
,以及如何克服它们。
17.3 在转译规格中常见的问题
转译在观念上简单,但实际上很混乱。即使你常常使用,语法仍然复杂而难解。
无论如何,如果你是初学者,最好的方式是你以别人的转译当作自己的转译的
基础。在指南页中有几个对xbiff、xdm(目前尚未介绍过,将於第20章介绍
)、xterm 的转译范例,将对你有所帮助。
如果你发现你的转译有错误的话,有几点值得去检查:
·转译只能应用在使用Toolkit的程式上。如果你试图对非Toolkit应用程
式定义转译,看起来不会有任何问题,只是转译不会作用而已。
让我们来看一下为什麽,以对xcalc (这是一个非Toolkit程式)使用转译
为例。你对一个resource名称像 *xcalc*translations定义一个转译表,
且用xrdb载入至你的资料库。xrdb并不会抱怨,因为它不知道是那一个
应用程式使用到resource,它只会设定资料库,稍後供Resource Manager
查询。现在你执行xcalc ,它对转译是一无所知,所以不会向资料库查
询转译,当然也绝不会编译它们了。
·不要省略 #override,除非你确实知道你要做什麽。如果你因错误省略
它,例如在xedit 中,你将发现没有任何的键可输入任何的东西 (因为
预设的转译 ":insert-char()"被去掉了)。
·检查你对每一列均有终结。如果你在转译表中的一列忽略了"n"或"n"
,在其後所有的转译都会被忽略。如果你在最後一列的末端加上一个倒
斜线() ,或是省略了档案中最後一个新列字元(newline),整个转译表
都会被忽略。(不过这是xrdb的问题,而非转译管理器的问题)。
这种错误在你编辑一个现存的转译表时特别容易发生。
·当你定义的转译和预设有冲突时,可能会导致奇怪的行为,特别是对滑
鼠按钮事件,每一次按下或"Down"事件,会相关到一个松开或"Up"事件
,当你对此部份没有明确定义时,可能会有一个预设的系结仍然存在,
(键盘的按下和松开也是成对的事件) .所以:
1.检查预设系结的文件。
2.如果你只对按下/松开配对的一半指定一个转译,确定另一半并非预设
转译的一部份,如果是的话,需对它明确地指定一个转译。
3.如果你仍然不能解决,暂时由表中移去#override,这将去掉所有的预
设转译,让你了解问题是由於和预设转译冲突所造成,还是因为你的
转译表有错误。
·转译管理器对语法不正确的问题,无法很好的告诉你原因何在。例如如
果你有一个转译像:
F6: string("abc""def")
叁数的语法并不正确,F6键将没有作用,但你也看不到错误讯息。
·如果你转译一序列的事件,且需要对每一个均指定修饰键,你必需明确
地对每一个都指定。例如如果你需要一个转译使用ctl-X ctl-K:
Ctrl X, Ctrl K: ...
而如果你使用:
Ctrl X, K: ...
你的指定为 ctl-X K
·检查你所需的widget是否有你指定的名称和类别。例如对xterm ,你可
以在一个表的开头指定:
xterm*Text*translations:
这将什麽事也没作,xterm 正规视窗widget的类别VT100。通常,不论
xrdb或转译管理器均不会有反应,因为看起来没错。
·转译可能指定正确,也可以工作,但它的作用和你预期的不符。例如对
xterm 的转译:
Meta Ctrl m: mode-menu()
是正确的,且会工作。但mode-menu()实际上检查滑鼠左或中按钮是否有
招唤它,其它方面不做任何事。
·在一个转译中不指定修饰键,并不意味着当修饰键按下时转译会无效。
它真正的意义为: "我并不在 有没有修饰键" 。如果需要的话,使用
"None",""或!符号。使用时要小心预设的转译是否会妨碍到你。
·转译是针对widget而指定的,所有在转译中的作用必需由widget提供。
在你指定转译resource名称的地方很容易忘掉这一点。例如:
xman*translations:
: reset()n
: set()
将导致许多错误:set()和reset()作用只有被Command widget定义,但
xman有数种其它型态的widget可接受转译,且转译管理器会抱怨这些
widget并未提供set()和reset()。解决之道为更完整些的指定resource
名称,例如在本例为 xman*Command*translations 。
·对任何给定的resource,当resource资料库被询问时,Resource Manager
会传回一个值给widget,这个传回的值的 "特徵值" (characteristic)
(resource名称)大多与widget的和属性的完整类别/成员名称相符。所
以你对所有的Text widget指定一个一般性的转译後,又对xedit指定一
个转译,希 它们并存是不可能的,只有一个转译表会传给widget。例
如:
*Text*Translation: #override
(对Text一般性的转译)
...
xedit*Text*Translation: #override
(对xedit 的Text特定的转译)
...
你只能得到在xedit 中特定的转译,或是在别处得到一般性的转译。
#override 会有所混淆,它的意义为 "把转译加入现存的之中" 。但这
完全由转译管理器处理,当时候到时,转译管理器会决定传递哪个值给
由Resource Manager所造的widget。对Resource Manager而言,#override
只是传递给widget值的部份中的一个文字字串而已。
因为你使用resource来指定转译,所以错误可能在两个领域均会发生。为
了减少错误的范围,当你对转译颇有经验时,在你已载入转译resource之後,
最好能明确地列印出你的resource资料库。例如:如果你对xprog 写入转译,
且转译在档案mytrans 中,以下列命令来执行程式:
xrdb mytrans ; xrdb -q ; xprog ...
php爱好者站 http://www.phpfans.net c/vc/c++/java.
电脑的键盘通常含有一些 "特殊功能" (special function)键,在此有一
些方法来 "制定"(program)这些特殊功能键,使它们能完成特定的功能以适合
你工作的方式。例如,你可以定义一些键来输入那些你常用的命令,或只需按
一个键便能够输入一些程式的片段。
在X中,你能制定的不只是功能键而已,其它一般的键和滑鼠的按钮也都
可制定。对每一个应用程式,你均可指定特别的功能给键盘和滑鼠按钮,或两
者之组合。 (例如在 xedit中你可以结合SHIFT 键和滑鼠的右按钮来让你向前
移动一个单字)。 所有使用X Toolkit的程式均允许使用者利用一个被称之为
键盘转译 (translations) 的设施来执行此种定义,且此种定义藉着正规的
resources 结构传递给应用程式。 (那些不使用X Toolkit的应用程式,同样
地也可以用相同的设施来制定, 但它们需个别的定义所以不能广泛地应用,从
现在起,我们假设每当讨论有关转译的种种,均为对那些使用X Toolkit的应
用程式而言。)
就如同所有的 resources一样,转译是当应用程式执行时才被处置。例如
你可以拥有数个具备不同转译设定的 xedit,在同时一起执行。( 你可以让一
个xedit 适合编辑本文,另一个适合编辑程式码,而另一个适合编辑文书)。
本章讨论转译 -- 包刮它们的定义格式,如何将它们设定到应用程式,和
它们所涵盖功能的范围。我们首先以实例来介绍,逐渐地导引你看到不同的角
度。而後比较正式和详细地讨论转译。最後,我们列出当你使用转译时常会碰
到的错误,并给你一些如何克服这些问题的提示。
17.1 实际使用转译
Toolkit 转译结构最简单的用途便是让你制定你键盘的键。例如,当你使
用xterm 为一个执行一般shell 命令的视窗时,你可能希 定义一些特殊功能
键来输入你常用的命令,且希 指定的关系如下:
当我按下这个键时... 我希 这个字串被输入
F1 rm core *.tmp
利用Toolkit 达到此目的方法为:指定一个值给使用转译的widget中的resources
。此值设定应用程式中所必需的定制 (customisation),且被Toolkit 的 Trans-
lation Manager (转译管理器) 所处理。此 resources属於类别Translation,
且其成员名称几 一定是translations。
在下一节我们将看到设定到resources 中的值的格式。
17.1.1 如何对一个应用程式指定转译
对前述xterm 的例子,我们定义 (在即将被应用程式读入的resource资料
库中或一些resource档案中) 一个规格类似:
xterm*VT100*Translations: (contd.)
F1: string("rm core *.tmp") 注意:不完整!!
其意为在任何类别 VT100的 xterm widget 中,当键 F1 被按下时, 入
字串 "rm core *.tmp")。
不幸的是,并没有这麽简单,转译管理器会把上面的规格解释为 "去掉所
有现存的转译,且加入... " ,所以所有正常的像 " A键是 入一个 A" 这种
系结 (binding)都会消失。为了克服这点,你必需使用一些被称为 directive
(指引)的语法 入在 resource 值之前:
xterm*VT100*Translations: #override(contd.)
F1: string("rm core *.tmp")
通常你会希 保持大部份现存的系结,而只是把你明确指定的值覆盖上去
,所以你一般都是在你的转译表中,指定 #override。
现在这个规格可以开始工作了,藉着启始一个 xterm且把此规格 (在两个
单引号 ()中间的部份) 当成选项 -xrm 的引数来测试它:
xterm -xrm xterm*VT100*Translations: ... 等等
按下特殊功能键 F1,你将看到指定的字串成功的 入,但并未包含新列字元
(newline) ,你可以用一点语法的技巧来克服它,像:
xterm*VT100*Translations: #override(contd.)
F1: string("rm core *.tmp")string(0xd)
这解释了以下两点:
1. string()的作用和它的引数相关。你可以直接输入本文 (例如 string(lpq))
,但如果本文包含空白或非字母字元,则必需在本文前後加上双引号。
如果引数是以 "0X" 开头,则将其後解释为十六进位,并 入相对的
ASCII 字元。(例如,0xd是RETURN)
2. 在你指定此功能时可结合一个以上的作用,在上例,我们用到 string()
作用两次,如果我们知道其它的作用,我们也一样可以系结起来。
定义许多的转译在一起
你可以根据需求在一个表中定义许多的转译。假设,我们在前面的转译中增加
系结:
当我按下这个键时... 我希 这个字串被输入
F2 lpq-Plpa3
对此的转译为:
F2: string("lpq-Plpa3")string(0xd)
所以可以将本列加入前面的表中。但是转译管理器的格式规则告诉我们必需将
两个转译以 "n" 分开且独立成为一列:
xterm*VT100*Translations: #override(contd.)
F1: string("rm core *.tmp")sting(0xd) n(cond.)
F2: string("lpq-Plpa3")string(0xd)
以上的形式将造成管理上的困难,你可以藉着包含 "隐藏的新列字元" 来使它
具可读性一些: (新列字元以倒斜线 ""处理)
xterm*VT100*Translations: #overriden
F1: string("rm core *.tmp")sting(0xd)n
F2: string("lpq-Plpa3")string(0xd)
你可以放置任意多个你所需的 "隐藏的新列字元" , 且几 在任何地方均可,
它们只是被忽略而已。 (只要和转译管理器相关,甚至你每隔一个单字便使用
一个也没关系。但千万不要在一个规格的 resource 特徵部份使用它们,它们
无法被resource manager解释,也没有相同的效果。) 如果你感觉有些混淆,
不用担心。简单地说,resource结构需要的是要在一列中的一个resource规格
的 "值" 的部份,而转译管理器以分开的列来分开 (也就是以n终结),而使用
者刚好以每一个实际分开的列代表一个意义以增加可读性,所以规则很简单:
在除了最後一列的每一个转译列均加上一个 "n"。
17.1.2 转译可系结许多型式的作用
上述的 xterm范例,展示了如何能够当你按下一个键时, 入任意的字串。但
转译结构的功能比这更多 -- 它可以系结任何widget所提供的作用到按键,让
我们详细一点地看一下这些作用。
前述的例子,我们在 xterm的 VT100 widget完成了键F1和F2在 string()
上的对应。我们将仍以 xterm为例,说明更多的作用。
查阅xterm 的指南页,在标题KEY TRANSLATIONS 和 KEY/BUTTON BINDINGS
你将发现列有数个作用。我们将定义一个转译对应键F3到insert-selection()
作用之上,所以我们可以用键盘来取代滑鼠,将先前 "剪"下的本文 "贴"出。
指南页告诉我们此作用需要一个引数,从列出的预设系结,我们可以看出预设
的 "剪贴" 结构为使用 CUT_BUFFER0, 所以我们将CUT_BUFFER0当作引数。我们
的resource规格是:
xterm*VT100*Translations: #overriden
F3: insert-selection(CUT_BUFFER0)
到目前为止,这只是一点小小的便利。然而,假定说你花了许多时间在本文文
件上工作,你用 tbl格式化,你用 nroff在萤幕上预视 (preview)它们,用
troff 排版,且将输出送到你的一个用过滤器 (filter) 为tr2printer的印表
机上。设定转译为:
xterm*VT100*Translations: #overriden
F3: string("ed") insert-selection(CUT_BUFFER0)
string(0xd)n
F4: string("tbl") insert-selection(CUT_BUFFER0)
string("| nroff -man") string(0xd)n
F5: string("tbl") insert-selection(CUT_BUFFER0)
string("| troff -man -t | tr2printer") string(0dx)
xterm 会确定这些转译是以xrdb自资料库载入或是在一个resource档案中,并
加以处理。现在当你启始xterm,用滑鼠 "剪"取你所需的工作的档案名称。接
下来,便可按F3键编辑它,按F4键预视它,和按F5键在硬拷贝上排版它。
更多的widget作用范例 -- xbiff
查阅xbiff 的指南页:在ACTIONS 的标题下,你将看到Mailbox widget所支援
作用的名单。它惟一预设的转译为当你按下任何按钮时降下信件 (mail)的旗帜
(flag)(作用unset())。我们将设定转译让你以键盘来运用这些作用,将这些作
用对应到 "?" 和 "UP" "DOWN"两个方向键如下:
? check() 有新的信件吗?
UP set() 升起信件的旗帜
DOWN unset() 降下信件的旗帜
以下是相关的转译表:
xbiff*Mailbox*Translations: #overriden
?: check()n
Down: unset()n
Up: set()
以此测试之:用xrdb从你的resource资料库载入这些设定,然後启动xbiff ,
将指标移到视窗内。重复地按下Up和Down游标控制键以升起和降下信件旗
帜。
找出有哪些作用被提供
你对widget作用将和widget名称遭遇相同的问题:如何找出某个widget到底提
供哪些作用以及它们能做些什麽?同样地,没有一个完美的解答,但有一个合
理的方法来处理:
1. 查看应用程式的指南页。大多数的应用程式有它们自己专门的作用文件
。例如:xbiff 有一节叫做ACTION,而xterm 有两节关於转译和作用的
文件 -- KEY TRANSLATIONS 和 KEY/BUTTON BINDING。
2. 最初的指南页可能给你提示,或甚至直接告诉你它用到何种widget的类
别,所以你可以查看它的widget set文件中的特定的widget。(在 core
版中惟一的widget set为Athena,所以你在此不易出错)。即使指南页未
告诉你widget的类别,当你对系统熟悉之後,你将对一个widget是否为
标准型态较具有概念,如果还是不行 ...
3. 查看程式的原始码,看看用到什麽widget的类别,以及widget提供了哪
些作用。
17.1.3 转译系结作用到一序列事件,不只是单一键
我们已经看到转译让你设定 入,转译结构也能让你系结这些作用:它可以是
单一的键,或是一序列的键,或者是事实上一序列任何的X事件。
让我们继续以xbiff 为例,看看如何转译一序列的键盘字元。例如我们定
义字元字串的转译如下:
look check()
raise set()
lower unset()
以下为相关的转译表:
xbiff*Mailbox*Translation: #overriden
l,o,o,k: check()n
r,a,i,s,e: set()
l,o,w,e,r: unset()
以此测试之 -- 载入设定和启动xbiff ,将指标移到视窗内。现在你可藉
着输入完整的字串来升起和降下旗帜。例如键入五个字元 r, a, i, s, e以升
起旗帜。对xbiff 的两个表有几点值得说明:
. 键的名称可以用不同的方式指定。正常的印出字元直接指定 (如"w")
, 其它的字元则拼出全名(如"Down").附录A "文件指引" 告诉你
在何处可查到键的名称,但在本节下面有更简单的方法。
. 对字元字串,你必需一一指定,并以逗点分开
(如"l,o,o,k").
. 转译可允许相同开头的键,例如 "look" 和 "lower" 均拥有相同的开头
"lo",对转译管理器不会形成问题。
找出键的名称
找出转译所需的键的名称,最简单的方法为执行xev ,将指标移到视窗内,按
下你所需的键,则键的名称会出现在括弧内字串keysym和一个十六进位数之後
。例如在xev 的视窗内按下游标控制键DOWN,它的输出如图17 - 1,在其中你
会看到
(keysym 0xff54, Down)
也就是说,键的名称为Down。
┏----------------┓
│ P209. fig 17 - 1 │
│ │
│ │
│ │
│ 图 17 - 1 xev 显示键名称的输出 │
┗----------------┛
你可以在转译中使用任何型态的事件
到目前为止,我们所写的转译都是系结作用到一个按下的键盘字元。但我们曾
说过,转译结构可系结作用到任何事件,而不只於按下键盘而已。可能的事件
型态非常的多,在此我们只提及一小部份:
型态 意义
按下一个键
按下一个键 (只是另一个名称)
放松一个键
按下一个滑鼠按钮
放松一个滑鼠按钮
指标进入视窗内
指标移出视窗外
我们已经使用过按下一个键的事件,让我们系结xbiff 作用到滑鼠按钮以取代之:
xbiff*Mailbox*Translations: #overriden
Button1: unset()n
Button2: check()n
Button3: set()
你可以看到语法和前面相似:你先给定一般性的事件型态 (例如 或
),其後跟着你所需事件的事件细节部份 (例如s 和Button3) (
Button 1, 2, 3分别对应到左、中、右按钮)
对一序列的事件的转译
就如同我们定义了一序列按下键事件的转译(set,unset和check),我们当然
也可以定义一序列的滑鼠事件。事实上你转译的一序列的事件可以任意组合在
一起,你可以在一个转译的左边随意混合事件的型态。所以你可以定义如下的
转译表:
xbiff*Mailbox*Translations: #overriden
Button1, ?, Button3: check()n
Button1: u, Button3: unset()n
Button1: s, Button3: set()
也就是说,用到check(),你必需依序先按下按钮1 (左按钮),然後按下"?"
键 ,最後按下按钮3 (右按钮)。 这个范例并不是很好,但对於一些危险或不
可取消 (irreversible) 的作用 (例如删除一个档案,或是覆写一个缓冲区的
内容) ,你可以依照这种方式来使用转译。你需要使用一个非常谨慎的命令序
列,才能用到此作用,这样使得使用者不可能因意外而输入此命令。
使用非键盘和非滑鼠事件的转译
通常你是对按下或放松滑鼠按钮或键盘的键定义转译。但我们曾经说过,你可
以对任何事件设定转译,例如指标移入或移出一个widget的视窗。让我们以
xman的主选项视窗 (图10 - 3) 为范例来解释它。这是一个相当人为的范例,
因为它没有任何用途。但无论如何,它很容易被看出在做些什麽操作。
查看xman的指南页,在X DEFAULTS标题下,你将看到概括的xman所用到的widget
的名称和类别:主选择项视窗widget的名称叫topBox,类别名为Command。这
是一个好的猜测,因为在选单操作盒的方法。我们可用第15章所提过的技巧
来确认它,使用以下的命令:
xman -xrm "*Command*backgroundPixmap: scales"
且所有的Command widget将会有鱼鳞状的背景)。
这和我们先前的范例有一个重要的不同:我们所用到的作用不是由特定的应用
程式指定,而是由标准的widget提供 (本例中为Command widget,在 "X
Toolkit Athena Widget"使用手册中有描述)。
在我们定义任何东西之前,先来看一看此widget预设的功用,以便我们能
够了解有些什麽事发生和有哪些widget的作用会做。启动xman,移动指标进入
Help 盒,你会看到盒的外框变成高亮度 -- 这是highlight() 在作用。将指
标移出,盒的外框恢复正常 -- 这是unhighlight() 作用。将指标再度移入
Help 盒,按下一个滑鼠按钮,保持按住不放。则盒内的色彩反转 (盒内的文
字变成预设的背景色,而原来视窗的背景变成视窗的前景色)。-- 这是set()
在作用。继续保持按住滑鼠按钮,将指标移出视窗外,盒内色彩恢复正常 --
这是reset() 在作用。一个正常 "碰触一下" (clicking on) Help盒的次序为:
1. 移动指标进入盒中:highlight()将外框变为高亮度。
2. 按下按钮:set() 反转盒中的色彩。
3. 松开按钮:notify()开始作用,造成程式建立求助视窗(help window)
。在进行中时,盒的色彩保持反相。当视窗建立完成之後,reset()反
转盒内的色彩为正常,但外框仍保持高亮度。
4. 将指标移出视窗:unhighlight()将外框恢复正常。
现在你了解了有哪些作用,我们将定义一些转译来改变原先进出视窗的作
用:
*Command*translations: #overriden
: reset()n
: set()
用这个奇怪的转译表,当你一开始移动指标进入盒中,什麽事也不会发生,但
当你移出指标时,色彩会反转。如果你再度移动指标进入盒中,色彩会变回正
常。其它的作用和前述相同。
使用修饰键来修饰事件规格
有时你指定的转译希 能同时按下一或多个修饰键 (modifiers) ,例如你要系
结一个作用到和META键同时按下的一个键,或是当CTRL和SHIFT 同时按下的滑
鼠按钮。到目前为止我们还没有任何办法可指定如此。我们不能用事件序列达
成这点,因为它是依序定义的,而我们需要的是指定同时,例如 "按下X 键且
CTRL键同时被按下"。
欲在转译中指定修饰键,你只需在事件名称之前加上你所需的修饰键名。
例如在xterm 中,定义meta-i为 "贴" 上一次 "剪" 的本文,使用:
*VT100*Translations: #override
Meta i: insert-selection(PRIMARY, CUT_BUFFER0)
因为这种修饰键/事件型态的组合十分常见,转译管理器允许使用一种缩写的
形式。相等於上面第二列的写法为:
i: insert-selection(PRIMARY, CUT_BUFFER0)
我们可以对滑鼠事件做同样的处理。让我们对xedit 定义转译,使得使用
滑鼠可以在本文上方便地移动,我们首先的尝试如下:
*Text*Translation: #override
Shift : forward-character()n
Shift : forward-word()n
Shift : next-line()n
Ctrl : backward-character()n
Ctrl : backward-word()n
Ctrl : previous-line()
如果你测试它,奇怪的现象会发生 -- 游标好像会自行其是,而且本文的片段
会一下子被选择,一下子又取消选择。发生这种现象的原因是Text widget 的
预设系结仍然会作用,它包含的转译像:
: extend-end(PRIMARY, CUT_BUFFER0)
你可能认为这不会影响你,因为当你松开按钮时你总是按着SHIFT 键或CTRL键
。但事实上会作用:转译管理器对於你未定义的修饰键解释为你不在 它们的
影响,所以松开Button1 时会对应到上述的规格。为了克服这点,我们对那些
可能不小心便会发生的按钮松开事件定义转译,并系结到一个空 (null) 作用
。这些转译当被对应到时会盖掉预设的转译。对使用Text widget 我们需再增
加两列,才是一个完整的转译表:
*Text*Translation: #override
Shift : forward-character()n
Shift : forward-word()n
Shift : next-line()n
Ctrl : backward-character()n
Ctrl : backward-word()n
Ctrl : previous-line()n
Shift : do-nothing()n
Ctrl : do-nothing()
这解释了下列几点:
·我们对滑鼠事件使用了缩写的语法,也就是先前的语法像Button1
以取代。转译管理器容许一些缩写的语法存在。 (我们在前
面看到的 也是一例)。
·我们用 do-nothing() 当作一个哑 (dummy)作用,就好像它是列在Text
widget的文件中一般。事实上这个作用是不存在的,因此会导致错误的
讯息出现,但因为我们本来就是要用它来什麽事也不做的,所以无需介
意。
·对於我们方才指定的哑作用,我们用了一个事件 便代表了三个
按钮。相同地,转译管理器把从缺的修饰规格的解释为 "对任何",在一
个事件中缺少细节部份 (例如在规格"Button1"中"Button1"的部
份) 解释为 "对任何所有的细节部份"。
这点在转译中有一个非常常用的形式为:
: ...
因为缺少细节部份,所以可被用於所有按下键 (key-press)事件,也就
是对所有的键。事实上在Text widget 上有一个预设的转译为:
: insert-char()
insert-char()作用的功能为当一个键被按下时, 入相对应的ASCII字
元。
17.1.4 复合的转译表及范例
到目前为止,我们把所有的转译均应用於整体的widget类别。但你能对个别的
widget指定转译,就如同resource一般。在此我们将对xman定义更多的转译。
我们将对Help盒widget (对应作用到助忆(mnemonic)字元)只用到键盘事件,对
Quit盒只用到视窗事件。为了达到此点,我们将对转译应用到的widget 给予明
确的名称。我们的转译表如下:
*Help*translations:
h: highlight()n
u: unhighlight()n
n: notify()n
s: set()n
r: reset()n
LineFeed: set() notify()
Quit*translations: #overriden
: reset()n
: set()
有几点特别的语法需要注意:
·在此我们对相同类别中不同的widget指定不同的转译,所以我们需要知
道成员 (instance) 名称。不幸的是,这些成员名称 (Help,Quit,
Manual Page)并不明显。如果它们在文件中找不到 (本例即找不到),那
你只能用猜的或是去查看原始程式了。
·对於Help,我们省略了常用的#override,因为我们对此widget不需要考
虑任何预设的系结。特别的是,当指标进入视窗时,我们不要此widget
呈现高亮度,如此我们才能看出这个转译的效用。
·由於省略 #override,我们将这个转译规格移至第一列。(如果不这麽作
,而且对第一列仍以n 作结束,我们将得到错误:
X Toolkit Warning: translation table syntax er-
ror: Missing : after event sequence.
X Toolkit Warning: ... found while parsing
因为n是用来区隔转译规格或类似像 #override 指令的)。而将此列和
第一个规格以隐藏的新列字元区隔,就如同:
*Help*translations:
h: highlight()n
...
·对LineFeed那一列的转译,包含了复合的作用,和前面xterm 中复合的
string()作用类似。
我们已大致涵盖了你所常用的转译。基本的概念很简单,生成它们的结构
也不复杂,但它们非常的令人迷惑。原因是它是一个发展中的糸统,随着发行
版本所附的文件并不是很多。下一节重复本节所述的,本节均以例子来介绍观
念,下一节我们将对转译的规则,作比较正式的描述。
17.2 转译 -- 格式和规则
转译是一个由Toolkit 提供的一般性结构,它让使用者指定当某些特定的事件
由widget接收到时,一个widget应完成何种作用。Toolkit 中处理转译的部份
被称之为转译管理器。
转译由widget指定,它的确是一个widget的每一个成员。一个转译的集合
称之为一个转译表,而这个表藉着标准的resource结构传递给应用程式。widget
(对转译而言意味深长) 会有一个Translation 类别的resource属性,通常的
成员名称为translation。这个转译resource期待的一个值即为一个转译表。就
像所有其它的resource一般,你可以在同一个应用程式对不同的widget指定不同
的resource,而且你能以类别名称或成员名称或二者混合来指定它们。
每一个widget定义了它所提供的作用,不论是在数量或型态上,它们都是
极富变化的。
转译可被各种不同型态的事件指定,不仅只於键盘和滑鼠事件而已。任何
序列的事件均能被处理,就如同单一事件一般。
转译和转译表在 " X Toolkit Intrinsics " 使用手册附录B一节中,有
简洁地描述。它不是初学者查看转译的好地方,但它含有完整的事件型态、修
饰名称等等的表列,在此不再赘述。
17.2.1 转译表的格式
一个转译表大体上的格式如下:
[optional-directiven] list-of-translations
每一个 list-of-translations 由一或多个转译组成,格式如下:
event-sequence : list-of-actions
当event-sequence发生时,规格中的list-of-actions 会由widget所完成。如
果在一个表中,有多於一个的转译,每一个需以 "n" 区隔开。
我们首先来看一下选项的指令,然後看一下list-of-translations的细节
部份。
17.2.2 转译指引 -- #override 等等
选项指引 (directive) 告诉转译管理器,它应对任何已设定之相关widget在此
转译集合中应如何处理。
#replace : 清除所有现存的对应,只采用在转译表中所含有的。(只使用新
的)。
#override : 强制留下现有的对应,加入转译表中。如果在表中有任何项
目设定,旧有的即被覆写。也就是说,旧有的被新有的取代。 (结合
旧有的和新的,但新的比较重要)。
#augment : 强制留下现有的对应,加入转译表中。如果在表中有任何项
目设定在现有的设定存在,使用旧的而忽略新的。 (结合旧有的和新的
,但旧的比较重要)。
如果未设定指引,预设为 #replace 。
17.2.3 个别的转译规格格式
每一个转译的格式为:
event-sequence : list-of-actions
让我们来看一看此规格的两个部份。
事件和事件序列(event-sequence)的格式
一个事件序列包含一或多个事件规格(event-specs), 其格式为:
[modifiers] [repeat-count] [detail]
除了事件型态(event type)外,均为可选择。(<>中为必需)。
modifiers : 这是基本设计中比较精巧的部份,我们在下一段说明。
event-type : 指定我们有兴趣的事件的型态,例如按键()、
松开按钮()或指标离开视窗()等等。
detail : 指定我们有兴趣的特定型态。如果你省略细节栏(detail field
),事件规格将对应到任何detail,如此,将对应到所有的按键
事件。此格式指定到每一个事件型态。对指定事件型态的细节栏为:
·对、和事件,细节如果不是键的名称(例如
"s"),便是 keysym (keysym是按键以开头为"0x"的十六进位
数表示,将於下一章详细解释)。
·对於按钮事件,细节就是按钮的名称,也就是 Button1 ... Button5
中的一个。例如我们先前使用过的"Button1"。
型态/细节的缩写:常用於转译管理器的一些事件型态和细节的组合,允许
你对它们使用缩写:
缩写 相等的全名
Button1
...
Button5
Button1
...
Button5
repeat-count : 这指定了事件需要的次数。如果被指定,它们被包含在
括弧之中。例如:
(2)
指定需对一号按钮(button-1)碰触两次。如果你在後面再加上加号(+)
,其意为碰触的数目需大於或等於指定。例如:
(3+)
意为需碰触三或更多次。预设的重复次数为一次。
一个事件序列以一或多个事件规格组成,以逗点分开。当这个事件的序列
在其widget发生时,相关的作用便会运作。
当序列发生时,转译管理器会根据一些规则决定它自己是否被满足。我们
用一个例子以便仔细地观察,假设你对两个字元序列set 和unset定义了转译:
·概略地说,如果个别的事件依序发生,转译管理器会被满足,其它的事
件 (那些你未指定的事件) 如果在指定的序列中间发生,不会妨碍序列
被满足。例如,set 可被sweat 和serpent 对应。
·如果介於其间的未指定事件,启动了转译表中的另一个事件序列,转译
管理器会放弃原先的序列,而尝试着去满足新的序列。例如,set 不会
被sauerkraut对应,因为u 会使得转译管理器对应到unset。
·如果在一个事件的集合中有超过一个的事件序列发生,转译管理器只会
应用到一个转译:
- 如果一个序列对应到结束 (右端),较短的那个序列只有在不包含於
较长的序列才会发生。所以如果unset 被对应到,对set 转译将不会
作用。
- 如果一个序列是在另一个序列的中间发生,例如,如果你定义序列
at和rate,则较长的那个永远不会被对应到。
事件修饰键
修饰键 (modifiers)是一些键或按钮,系指当主要事件发生时,那些必需被按
下才会让转译管理器满足的键或按钮。你可以对键、按钮、移动、进出视窗等
事件指定修饰键。常见的修饰键为:
Button1 ... Button5
Ctrl Shift Meta
Lock
如果你未指定任何的修饰键,转译管理器会解释为: "当事件发生时,不
论修饰键是否被按下,均会被接受"。例如,会被满足,不论当时
SHIFT 或META键是否有被按下。
如果你真的需要指定 "只有在没有修饰键被按下时才接受此事件"。则需使
用虚拟修饰键 (pseudo-modifier) None。例如,None 会使得当按
钮按下时若META键也被按下则不会满足。
对一个事件指定一些修饰键意为 "只要符合转译中指定的修饰键,其它的
修饰键不需介意"。它并没有 "一定要完全恰好符合才可以"的意思。例如,
Ctrl a 在你按下 meta-ctl-shift-a 时仍会被满足。
如果你真的要指定 "只有刚好符合修饰键的才要",在修饰键之前加一个惊
叹号 (!)。例如,!Ctrl a 在你按下 meta-ctl-shift-a 时不会被满足。
对一个修饰键的集合 (可能是空集合) 作限制,意为 "除了这些修饰键不
不接受",需要在不接受的修饰键之前加一个(~)号。例如,Shift~Meta t
会被ctl-shift-t满足,不会被meta-shift-t满足。
键事件通常忽略大小写,如果你要区分,需在之前加一个冒号(:)。例如,
不论 H或 h均可符合H,但只有H 才符合 :H。
就如同对常用的事件型态/细节配对有缩写一般,转译管理器对常用的修
饰键/事件型态配对同样地提供缩写:
缩写 相等的全名
Ctrl
Shift
Meta
Button1
...
Button5
任何按钮的
作用的格式和作用的表列
每一个转译在一或多个作用之上系结一个序列的一或多个事件。在表列中的个
别作用是以空白分开的。 (不可用逗点分开,那将会导致错误)。
个别的作用格式如下:
action-name(parameters)
即使没有叁数被指定,在作用名称 (action-name)後的括弧,仍然不可省略。
例如:
start-selection()
如果在作用名称和左括弧中间留有空白,你将会得到一个错误。
作用名称只包含了字母、数字、钱号($)、底线(_)四种字元。每一个widget
提供它自己的作用集合 (如果有的话) ,且自我包含这些作用名称的硬码
(hard-coded)表列。
叁数(parameters)是一个零到多个字元字串的表列,中间以逗点分开。叁
数的意义为对特定的作用作指定 (事实上大多数的作用并没有任何叁数) 。叁
数字串可以不加引号,例如:
insert-selection(PRIMARY)
或者前後加上双引号,这种情形通常为叁数字串内包含了空白或一个逗点,例
如:
string("plot")
没有一个一般性的方法,让你在叁数字串中的任何位置包含一个双引号,虽然
像这样 string(ab"cd)将双引号放在字串中间是可被处理的。也没有一般性的
方法在同一个叁数字串中同时包含字串和双引号。因为如此,有些widget在解
释它们自己的叁数时,可以自行加入它们自己的语法规则。例如:对xterm 的
VT100 widget的 string()作用,如果一个不带双引号且开头为 "0x"的字串,
此字串被解释为代表一个ASCII 字元的十六进位数。
在此结束我们对转译规格及格式的描述。由此,你应有能力了解在不同X
手册列出的转译,且可写你自己的转译。为了帮助你,下节列出你常见的问题
,以及如何克服它们。
17.3 在转译规格中常见的问题
转译在观念上简单,但实际上很混乱。即使你常常使用,语法仍然复杂而难解。
无论如何,如果你是初学者,最好的方式是你以别人的转译当作自己的转译的
基础。在指南页中有几个对xbiff、xdm(目前尚未介绍过,将於第20章介绍
)、xterm 的转译范例,将对你有所帮助。
如果你发现你的转译有错误的话,有几点值得去检查:
·转译只能应用在使用Toolkit的程式上。如果你试图对非Toolkit应用程
式定义转译,看起来不会有任何问题,只是转译不会作用而已。
让我们来看一下为什麽,以对xcalc (这是一个非Toolkit程式)使用转译
为例。你对一个resource名称像 *xcalc*translations定义一个转译表,
且用xrdb载入至你的资料库。xrdb并不会抱怨,因为它不知道是那一个
应用程式使用到resource,它只会设定资料库,稍後供Resource Manager
查询。现在你执行xcalc ,它对转译是一无所知,所以不会向资料库查
询转译,当然也绝不会编译它们了。
·不要省略 #override,除非你确实知道你要做什麽。如果你因错误省略
它,例如在xedit 中,你将发现没有任何的键可输入任何的东西 (因为
预设的转译 ":insert-char()"被去掉了)。
·检查你对每一列均有终结。如果你在转译表中的一列忽略了"n"或"n"
,在其後所有的转译都会被忽略。如果你在最後一列的末端加上一个倒
斜线() ,或是省略了档案中最後一个新列字元(newline),整个转译表
都会被忽略。(不过这是xrdb的问题,而非转译管理器的问题)。
这种错误在你编辑一个现存的转译表时特别容易发生。
·当你定义的转译和预设有冲突时,可能会导致奇怪的行为,特别是对滑
鼠按钮事件,每一次按下或"Down"事件,会相关到一个松开或"Up"事件
,当你对此部份没有明确定义时,可能会有一个预设的系结仍然存在,
(键盘的按下和松开也是成对的事件) .所以:
1.检查预设系结的文件。
2.如果你只对按下/松开配对的一半指定一个转译,确定另一半并非预设
转译的一部份,如果是的话,需对它明确地指定一个转译。
3.如果你仍然不能解决,暂时由表中移去#override,这将去掉所有的预
设转译,让你了解问题是由於和预设转译冲突所造成,还是因为你的
转译表有错误。
·转译管理器对语法不正确的问题,无法很好的告诉你原因何在。例如如
果你有一个转译像:
F6: string("abc""def")
叁数的语法并不正确,F6键将没有作用,但你也看不到错误讯息。
·如果你转译一序列的事件,且需要对每一个均指定修饰键,你必需明确
地对每一个都指定。例如如果你需要一个转译使用ctl-X ctl-K:
Ctrl X, Ctrl K: ...
而如果你使用:
Ctrl X, K: ...
你的指定为 ctl-X K
·检查你所需的widget是否有你指定的名称和类别。例如对xterm ,你可
以在一个表的开头指定:
xterm*Text*translations:
这将什麽事也没作,xterm 正规视窗widget的类别VT100。通常,不论
xrdb或转译管理器均不会有反应,因为看起来没错。
·转译可能指定正确,也可以工作,但它的作用和你预期的不符。例如对
xterm 的转译:
Meta Ctrl m: mode-menu()
是正确的,且会工作。但mode-menu()实际上检查滑鼠左或中按钮是否有
招唤它,其它方面不做任何事。
·在一个转译中不指定修饰键,并不意味着当修饰键按下时转译会无效。
它真正的意义为: "我并不在 有没有修饰键" 。如果需要的话,使用
"None",""或!符号。使用时要小心预设的转译是否会妨碍到你。
·转译是针对widget而指定的,所有在转译中的作用必需由widget提供。
在你指定转译resource名称的地方很容易忘掉这一点。例如:
xman*translations:
: reset()n
: set()
将导致许多错误:set()和reset()作用只有被Command widget定义,但
xman有数种其它型态的widget可接受转译,且转译管理器会抱怨这些
widget并未提供set()和reset()。解决之道为更完整些的指定resource
名称,例如在本例为 xman*Command*translations 。
·对任何给定的resource,当resource资料库被询问时,Resource Manager
会传回一个值给widget,这个传回的值的 "特徵值" (characteristic)
(resource名称)大多与widget的和属性的完整类别/成员名称相符。所
以你对所有的Text widget指定一个一般性的转译後,又对xedit指定一
个转译,希 它们并存是不可能的,只有一个转译表会传给widget。例
如:
*Text*Translation: #override
(对Text一般性的转译)
...
xedit*Text*Translation: #override
(对xedit 的Text特定的转译)
...
你只能得到在xedit 中特定的转译,或是在别处得到一般性的转译。
#override 会有所混淆,它的意义为 "把转译加入现存的之中" 。但这
完全由转译管理器处理,当时候到时,转译管理器会决定传递哪个值给
由Resource Manager所造的widget。对Resource Manager而言,#override
只是传递给widget值的部份中的一个文字字串而已。
因为你使用resource来指定转译,所以错误可能在两个领域均会发生。为
了减少错误的范围,当你对转译颇有经验时,在你已载入转译resource之後,
最好能明确地列印出你的resource资料库。例如:如果你对xprog 写入转译,
且转译在档案mytrans 中,以下列命令来执行程式:
xrdb mytrans ; xrdb -q ; xprog ...
php爱好者站 http://www.phpfans.net c/vc/c++/java.
相关阅读 更多 +