xfy technology についての備忘録です。
要素を増やせるようになったので、今度は要素の種類を変更できるようにします。
DocBook 用のボキャブラリコンポーネントとして、下記の機能を実装します。
各要素が表示される (実装済み)。
各要素の内容 (テキストノード) を編集できる (実装済み)。
要素が空要素用の場合 → プレースホルダを示す文字列が表示される (実装済み)。
カレット位置に応じて要素の挿入、分割、追加ができる (実装済み)。
要素の種類を変更できる。
パラグラフをプログラムリストに、プログラムリストをパラグラフに、それぞれ変更できるようにします。前回の要素の分割処理とは異なり、これらの処理はコンテキストメニューから行うことにします。コンテキストメニューの項目からは、下記のコマンド (後述) を呼び出します。
パラグラフ: to-para コマンド
プログラムリスト: to-programlisting コマンド
:
<xvcd:vocabulary match="db:article" call-template="root" name="db:article">
<ui:ui xmlns:ui="http://xmlns.xfy.com/ui" version="1.0">
<ui:context-menu>
<ui:menu-item label="パラグラフ" command="to-para"/>
<ui:menu-item label="プログラムリスト" command="to-programlisting"/>
</ui:context-menu>
</ui:ui>
</xvcd:vocabulary>
:
この XVCD スクリプトを XML 文書に適用すると、次のようにコンテキストメニューが表示されるようになります (が、コマンドが未定義なので、コンテキストメニューの項目は非活性状態になっています)。
次に、コンテキストメニューから呼び出されるコマンドを定義します。このコマンド中で、処理対象の要素 (= カレット位置の要素) の内容を新しい要素にコピーし、その要素で処理対象の要素を置き換えます。といっても、 XVCD には要素を置き換えるためのコマンドインストラクションが用意されていないので、要素の挿入 (xvcd:insert 要素) と要素の削除 (xvcd:delete 要素) とで代替します。
:
<xvcd:command name="to-para">
<inst:variable name="programlisting"
select="xvcd:caret-node()/ancestor-or-self::db:programlisting[1]"/>
<xvcd:insert ref="$programlisting" position="before">
<xvcd:element name="para" namespace="{namespace-uri($programlisting)}">
<xvcd:copy-of select="$programlisting/child::node()"/>
</xvcd:element>
</xvcd:insert>
<xvcd:delete select="$programlisting"/>
</xvcd:command>
:
新しい要素の挿入位置は、処理対象の要素の直前 (position="before") が良いようです。処理対象の要素の直後に挿入すると (position="after")、処理対象の要素を削除した際にカレットが消えて (どこかへいって ?) しまいます。
同様に、 to-programlisting コマンドも定義します。
:
<xvcd:command name="to-programlisting">
<inst:variable name="para"
select="xvcd:caret-node()/ancestor-or-self::db:para[1]"/>
<xvcd:insert ref="$para" position="before">
<xvcd:element name="programlisting" namespace="{namespace-uri($para)}">
<xvcd:copy-of select="$para/child::node()"/>
</xvcd:element>
</xvcd:insert>
<xvcd:delete select="$para"/>
</xvcd:command>
:
この XVCD スクリプトを XML 文書に適用すると、次のように要素の種類を変更できるようになります。
処理対象がパラグラフの場合、コンテキストメニューから選べるのは [プログラムリスト] だけであるべきです。同様に、処理対象がプログラムリストの場合、コンテキストメニューから選べるのは [パラグラフ] だけであるべきです。処理対象の要素に応じてコンテキストメニューの項目の状態 (活性、非活性) が変わるよう、 UI を改善します。
XVCD では、 xvcd:command 要素の enabled 属性値または要素の内容に応じて UI の活性、非活性が切り替わります。ここでは後者の方法を採り、 xvcd:command 要素中の inst:variable 要素を inst:for-each 要素に置き換えることにします。
:
<xvcd:command name="to-para">
<inst:for-each select="xvcd:caret-node()/ancestor-or-self::db:programlisting[1]">
<xvcd:insert ref="." position="before">
<xvcd:element name="para" namespace="{namespace-uri()}">
<xvcd:copy-of select="child::node()"/>
</xvcd:element>
</xvcd:insert>
<xvcd:delete select="."/>
</inst:for-each>
</xvcd:command>
<xvcd:command name="to-programlisting">
<inst:for-each select="xvcd:caret-node()/ancestor-or-self::db:para[1]">
<xvcd:insert ref="." position="before">
<xvcd:element name="programlisting" namespace="{namespace-uri()}">
<xvcd:copy-of select="child::node()"/>
</xvcd:element>
</xvcd:insert>
<xvcd:delete select="."/>
</inst:for-each>
</xvcd:command>
:
inst:for-each 要素内では処理対象の要素がカレントノードになるので、 xvcd:insert 要素の ref 属性値や xvcd:delete 要素の select 属性値などの XPath 表記が簡潔になります。
この XVCD スクリプトを XML 文書に適用すると、次のようにコンテキストメニューの項目の活性、非活性が切り替わります。
例によって記述の重複を避けるために、 inst:for-each 要素の内容を別コマンド (コマンド名: replace-element) として切り出します。
:
<xvcd:command name="replace-element">
<inst:param name="target"/>
<inst:param name="with"/>
<xvcd:insert ref="$target" position="before" select="$with"/>
<xvcd:delete select="$target"/>
</xvcd:command>
:
replace-element コマンドにはパラメータが二つ備わっています (パラメータ名: target、 with)。このコマンドを呼び出す際は、処理対象 (= 置き換え対象) の要素を target パラメータに、新しい要素を with パラメータに、それぞれ指定して呼び出します。
:
<xvcd:command name="to-para">
<inst:for-each select="xvcd:caret-node()/ancestor-or-self::db:programlisting[1]">
<inst:call name="replace-element">
<inst:with-param name="target" select="."/>
<inst:with-param name="with">
<xvcd:element name="para" namespace="{namespace-uri()}">
<xvcd:copy-of select="child::node()"/>
</xvcd:element>
</inst:with-param>
</inst:call>
</inst:for-each>
</xvcd:command>
<xvcd:command name="to-programlisting">
<inst:for-each select="xvcd:caret-node()/ancestor-or-self::db:para[1]">
<inst:call name="replace-element">
<inst:with-param name="target" select="."/>
<inst:with-param name="with">
<xvcd:element name="programlisting" namespace="{namespace-uri()}">
<xvcd:copy-of select="child::node()"/>
</xvcd:element>
</inst:with-param>
</inst:call>
</inst:for-each>
</xvcd:command>
:
現時点での XVCD スクリプトです。
下記の情報を参考に、 xfy Personal Client 1.0 のメニューを Mac OS メニューバーに組み込んでみました。
xfy Basic Edition 1.0 では正しく組み込まれましたが、 xfy Personal Client 1.0 (xfy Basic Edition 1.3) では正しく組み込まれませんでした。残念…… (パラメータの指定が間違っているのかもしれません)
先のエントリーで上手くいかなかった Mac OS X メニューバーへの組み込みの件についてフォーラムにポストしたところ、 (動作保証外ですが) 対策方法を教えていただきました。
xfy Personal Client 1.0 では、 JVM を再起動しているそうです。この JVM の再起動を抑止することで、 java コマンドのパラメータが有効になるとのことでした。
それにしても、なぜ JVM を再起動しているのでしょう……