Skip to content

Latest commit

 

History

History
1116 lines (747 loc) · 48.9 KB

JAWS_Script_Tutorial.md

File metadata and controls

1116 lines (747 loc) · 48.9 KB

JAWS for Windows スクリプトチュートリアル

Copyright (C) 2013, 2021 Extra corporation, Masanori Kiriake

はじめに

JAWS for Windowsは、世界で最も多く利用されているスクリーンリーダーの一つです。この講座では、JAWS for Windowsをより便利に使うために、アプリケーションに合わせた設定方法やスクリプト言語の利用方法について学習します。

日々利用するような小さなアプリケーションのスクリプトが書けることを目標に、スクリプトの動作原理なども含めて学習します。

JAWS for Windowsについて

JAWSの歴史

JAWSは、1989年に、アメリカでテッド・ヘンターによって開発・リリースされました。彼は、自己で失明した後、視覚障害者がコンピュータを使って、一般に使われているソフトが使用できるようにすることを目標に開発を続けてきました。当時のJAWSはMS-DOS用で、既に今のJAWSスクリプトに当たる機能が搭載されていました。

1995年、Windows用のJAWSがリリースされました。当時日本にはWindows用のスクリーンリーダーは市販されておらず、主にMS-DOSを使って視覚障害者はコンピュータを利用していました。

日本語版のJAWSが初めてリリースされたのは、2001年春、JAWS Version 3.7で、欧米出身のスクリーンリーダーとしては二つ目となり、それまで日本のスクリーンリーダーには搭載されていなかった機能をたくさん紹介することになりました。この時期はIBM社からリリースされていました。

2005年、JAWS Version 6.2からは、有限会社エクストラ社からリリースされており、最新のコンピュータ環境に追従しより使いやすくすることを目指して、毎年1回のバージョンアップが行われています。

JAWS for Windowsの特徴

一般的特徴はJAWSのマニュアルやその他の資料に掲載してありますので、このテキストでは、スクリプト開発を行うという視点でJAWSの特徴を紹介します。

読み上げに使用する情報源が豊富である

スクリーンリーダーがなにかを読み上げるためには、読み上げるためのなんらかの情報が必用です。JAWSはあらゆる手段を使って、情報を取得します。読み上げに使われる可能性のある情報を以下に列挙します。

  • OSやアプリケーションからの情報: OSや、使用中のアプリケーションから読み上げに必用な情報を取得します。Windowsは、スクリーンリーダーのために読み上げに必用な情報を提示する機能を持っています。この機能を使ってJAWSは情報を取得して読み上げます。MSAA・UIA・IAccessibleという機能はその1例です。
  • 画面に書かれた文字情報: コンピュータで文字を表示するには、文字の形そのものをアプリケーションが表示するのではなく、どの文字を表示させるかという命令をOSに送ります。この部分に入り込んで情報を取得することにより、全く読み上げに必用な情報が得られなくても直接どの文字が画面上のどこに書かれたかを取得することができます。この機能により、JAWSは多くのアプリケーションの読み上げが可能になります。
  • 画像を認識して得られる情報: 上記二つのアプローチでも読み上げに必用な情報を取得できないとき、最終的な手段として画面に書かれた画像から文字認識を行い読み上げる機能があります。文字認識を行うという性格上、表示状態やフォントによって認識精度は異なりますが、十分実用的に使うことができる情報です。

JAWSの動作をカスタマイズ可能

JAWS自身もスクリプト機能を使って書かれています。このスクリプト機能は自分自身を変更するような使い方も可能です。初期状態で用意されている読み上げ機能ですら、アプリケーションに合わせて変更することができます。JAWSが取得したいろいろな情報をどのように提示するとわかりやすいかを考えて、アプリケーションに合わせて変更することができます。

JAWSの基本的な使用方法

スクリプトを書く前に、JAWSの基本的な使用方法、アプリケーションに合わせた設定方法について簡単に紹介します。実際にスクリプトを書く前にこれらの設定を行ってみるだけで、大きく読み上げが改善することもあります。

JAWSの起動と終了

通常JAWSはWindowsが起動すると同時に動き出すように設定します。なんらかの事情でJAWSを手動起動する必要がある場合は、下記の手順で行うのが確実でしょう。

  1. Windows+Mキーを押してデスクトップを表示します。
  2. Windows+Rキーを押して[ファイル名を指定して実行]のダイアログを表示します。
  3. JAWS14 のように、JAWSとバージョン番号を入力して、エンターキーを押します。

JAWSを終了するには、以下のように操作します。

  1. Insert+F4キーを押します。終了確認のダイアログが表示されます(設定によりこのダイアログを表示しないようにすることもできます)。
  2. OKボタンを押して、JAWSを終了します。コンピュータの処理能力にもよりますが、数秒間でJAWSは終了します。

基本的な読み上げコマンド

ここでは、JAWSを使用するに当たって、最もよく使うコマンドについて紹介します。Windowsの操作、ネットサーフィン中、メールの閲覧、またスクリプト作成中などあらゆるところで使用されるでしょう。

JAWSは、コマンドキーとしてテンキーを利用しています。テンキーのないコンピュータのためのモードもありますが、可能な限りテンキーを使うことをお勧めします。また、Insertキーと何かのキーを押すことで様々なコマンドを実行します。これをJAWSKeyと呼びます。テンキーを使っている場合は、テンキーの0をJAWSKeyとして使用することができます。

文字・単語・行の読み上げ

  • カーソル位置の文字の読み上げ: テンキーの5
  • カーソルのある行の読み上げ: JAWSKey+テンキー8
  • カーソル位置の単語の読み上げ(主に英文): JAWSKey+テンキー5
  • カーソルより左の読み上げ: JAWSKey+テンキー7
  • カーソル位置から右の読み上げ: JAWSKey+テンキー9
  • カーソル位置の文字の説明読み: テンキー5を2回

その他の読み上げコマンド

コンピュータの状態を確認したりするために使用する便利なコマンドを列挙します。

  • 現在操作中のアプリケーションの読み上げ: JAWSKey+T
  • 現在注目している部分の読み上げ(ダイアログ内であればどのボタンを注目しているかなど): JAWSKey+Tab
  • デフォルトボタンの読み上げ(エンターキーを押したときに動作するボタン): JAWSKey+E
  • 時刻の読み上げ: JAWSKey+F12
  • 起動しているアプリケーションの選択: JAWSKey+F10

その他様々なコマンドがあります。

カーソルの概念

JAWSには、その性質によっていくつかのカーソルが用意されています。このカーソルを理解し使いこなすことにより、これまでキーボード操作だけではできなかったことを実現することができます。

カーソルは、画面上のある位置を示しています。以下それぞれについて解説します。

PCカーソル

キーボードで操作しているときにフォーカスがある位置です。文書編集中であればキャレットがある位置、ダイアログボックスを操作しているのならば、ボタンやチェックボックスなどが表示されている位置、スタートメニューやExplorerなどを操作しているのならば選択されている項目が表示されている位置となります。

PCカーソルは、キーボード操作とともに移動し、基本的にはこの位置をJAWSは読み上げます。 テンキー '+' を押すとPCカーソルになります。これが初期状態です。

JAWSカーソル

マウスポインタがある位置です。JAWSカーソルモードでは、矢印キーはJAWSカーソルを動かすキーとして機能します。遠回しな言い方ですが、JAWSカーソルを動かすことによりマウスを動かすことができます。また、左クリック・右クリックなども実行することができます。

テンキー '-' を押してJAWSカーソルに切り替えます。JAWSカーソルモードでは、JAWSは結果としてマウスがある位置に書かれている物を読み上げることができます。マウス操作をする目的の他、画面上に書かれていることを確認するためにも使用されます。

レビューカーソル(Invisible cursor)に切り替える

JAWSカーソルと似ていますが、マウスとは連動していないことが異なります。マウスを動かしてしまうと表示状態が変化してしまうアプリケーションで画面上の内容を取得したいときに使います。

テンキー '-' を2回続けて押してレビューカーソルに切り替えます。マウスクリック操作を行うと、マウスとは連動していないため意図した結果にならないことがあるでしょう。

仮装カーソル

Microsoft Edge, Google Chrome, Adobe Readerなど、画面上に書かれている情報を、音声や点字で読んだときにわかりやすいように加工して表示(実際には画面には現れませんが)します。これを仮装バッファと呼びますが、この中を移動するときに使われるカーソルが仮装カーソルです。 仮装カーソルの画面上の位置は、注目している文字が実際に表示されている位置と言うことになります。

オブジェクトカーソル

このチュートリアルの最後の方でも触れますが、Windowsは論理的には画面上にたくさんの「オブジェクト」が表示されているOSです。これまでのカーソルは実際の画面イメージに近い動きをしていましたが、オブジェクトカーソルでは、その論理構造に従った動きをします。

実際の使用では、タッチスクリーンを搭載したコンピュータで、ジェスチャーによってJAWSを使っているときにオブジェクトカーソルが使われています。

シフトキーを押しながらテンキーの'+'を押して切り替えます。PCカーソルに戻すときはテンキーの'+'を2回押します。

各カーソルの切り替えなど

下記コマンドによりそれぞれのカーソル位置を交換したり切り替えたりします。JAWSの操作に慣れてくると頻繁に使うコマンドの一つです。

  • PCカーソル位置にJAWSカーソルを合わせる: JAWSKey+テンキー-
  • JAWSカーソル位置にPCカーソルを合わせる: JAWSKey+テンキー+

JAWSの設定方法

JAWSは様々な方法でアプリケーションに合わせた設定をすることができます。すべてを理解することはなかなか困難ですが、その中から特に使用頻度の高い設定やカスタマイズについて紹介します。

基本設定

読み上げ音声の設定や、JAWSの読み上げ詳細度の設定などを行います。

読み上げ音声の設定

音声で情報を聴き取ってコンピュータを操作するときに、聞きやすい状態に調整することはとても重要なことです。JAWSは様々な状態に音声を調整することができます。

音声の調整は、「音声プロファイル」を作成することで行います。

  1. JAWSKey+Jキーを押して、JAWSのウィンドーを表示します。
  2. メニューバー(あるいはコンテキストメニュー)から、[オプション]→[音声]→[音声調整]の順番で選択し実行します。
  3. ダイアログ内の[音声調整]コンボボックスで調整したい音声を選択します。
  4. それぞれのパラメータを好みに調整します。
  5. 初めて音声の調整を行う場合にはプロファイルを作成する必要があります。[名前をつけて保存]ボタンを押して新しいプロファイルを作ります。現在選択されているプロファイルを元に名前が入力されていますので、変更する必要がなければそのままエンターキーを押します。
  6. [OK]ボタンを押して設定を確定します。

なお、速度だけを変更する場合には以下のコマンドを使うことができます。

  • 速度を上げる: Ctrl+Alt+Windows+PageUp
  • 速度を下げる: Ctrl+Alt+Windows+PageDown

クイック設定を使う

最も簡単に起動して設定を行うことができる手段が『クイック設定』です。クイック設定は現在使用中のアプリケーションに対する設定を行います。一部の設定はクイック設定からしか行うことができない物もあります。

クイック設定は以下の手順で起動します。

  1. JAWSKey+Vを押します。「クイック設定 - アプリケーション名」のような読み上げが行われてダイアログが表示されます。
  2. 検索ボックスに設定項目名を入力するか、設定ツリービューから目的の設定を探します。
  3. スペースキーを使って設定値を変更します。
  4. [OK]ボタンを押して設定を保存します。

対象とするアプリケーションによりどの項目を設定するかは変化してきます。いろいろ試しながら、最適な設定を探していきます。

なお、クイック設定で設定した内容は基本的には保存されません。アプリケーションによってはこのクイック設定を使って設定を保存するようになっているものもあり、どれが保存されてどれが保存されないのかは、実際のところやってみたり当該スクリプトを読まなければわからないことがあります。

設定センターを使う

JAWSの初期設定を変更する場合や、より詳細な設定を行う場合には『設定センター』を使います。使用頻度はそれほど多くはありませんが、このツールでしか変更できない設定は多くあり、それらを徐々に理解することでより便利にJAWSを使うことができるようになるでしょう。

下記のようにして起動します。

  1. JAWSKey+Jキーを押してJAWSのウィンドーを表示します。
  2. メニューバーから、[ユーティリティ]→[設定センター]の順に選択、実行します。
  3. クイック設定と同じように、検索ボックスに設定項目名を入力するか、設定ツリーの中から目的の設定を探します。
  4. スペースキーを押して設定値を変更します。設定によっては新しいダイアログが開いて設定を行う物もあります。
  5. [OK]ボタンを押して設定を保存します。

それぞれの設定の詳細についてはマニュアルなどに譲ります。

プロンプト作成機能

アプリケーションによっては、Tabキーで項目を移動したとき、移動先の項目名を正しく読み上げることができない物が少なくありません。このような場合、根本的にはこれから学習するスクリプトを書いて対処するのですが、それほど複雑なアプリケーションではない場合にはここで紹介する『プロンプト作成』機能で代用できることがあります。実際に使ってみて、安定して読み上げが行えるようであれば、スクリプトを書く必要はないかもしれません。

プロンプトは下記のようにして作成します。

  1. 読み上げさせたい場所にPCカーソルを置きます。
  2. JAWSKey+Ctrl+Tabキーを押します。[プロンプト作成[のダイアログが表示されます。
  3. 画面の指示に従って、読み上げさせたい名前、点字表示させたい名前、簡単な説明を順番に入力して、[完了]ボタンを押します。
  4. 実際にTabキーやShift+Tabキーを押して、動作を確認します。

スクリプトを作ってみよう

それでは、スクリプト制作の実際について学習していきましょう。JAWSスクリプトは一種のプログラミング言語です。「言語」というからには自分でしゃべらなければ上達しません。最初は実用的な会話ができなくても、とにかく使い続けることで言葉は習得できる物です。プログラミングも同じようなところがあり、最初は実用的な物は作れなくても、それを繰り返すことで少しずつ実用的なプログラムが書けるようになります。

スクリプトはいつ動くのか

JAWSのスクリプトは、小さなスクリプトの集まりです。それぞれのスクリプトにはごく小さな処理だけが記述されています。それぞれが適切なときに動作することによって全体としてアプリケーションの読み上げ処理を行っています。それでは、小さなスクリプトはいつ動作するのでしょうか。

キーボード入力に反応して動作するもの

ショートカットキーを割り当てたスクリプトは、そのショートカットキーが押されると動作します。例えば、あるショートカットキーを押すと画面の特定の場所に書かれた情報を読み上げる、という用途で使用します。エディターならば画面の一番下に書かれているページ数や行数・文字数などの情報を読み上げるという使い方です。また、あるショートカットキーを押すと一連の操作を行わせるためにも使用します。例えば画面上にあるマウスでしか操作できないボタンを探してクリックする、といった使い方をします。

JAWSが自動的に呼び出す物

JAWSは、イベントという形で、特定の名前のスクリプト(正しくは関数)を呼び出します。画面に文字が書き込まれたとき、画面上の注目位置(フォーカス)が変化したとき、アプリケーションが起動したり終了したりしたときなど、様々なイベントがあります。このイベントに適切な処理を記述することで読み上げ対応を行います。

例えばダイアログ内でTabキーを押すとフォーカスが変化しますが、このときJAWSはイベント関数を呼び出します。このイベント関数に読み上げ対応を書き込むことで移動先のボタンや入力フィールドの名前を読み上げさせることができます。

少し高度な使い方になりますが、実用的なスクリプトを書くためにはイベントを習得することが必用でしょう。

スクリプトを書いてみよう

それでは、実際にスクリプトを書いてみましょう。まずは、メモ帳を起動して、メモ帳のためのスクリプトを書いてみましょう。

スクリプトマネージャーを使う

JAWSスクリプトは、いわゆるテキストファイルです。メモ帳やその他のエディターでも編集することができますが、JAWSにはスクリプトを編集する機能を持った「スクリプトマネージャー」が付属しています。特別な事情がない限りこのエディターを使うことをお勧めします。

それでは、スクリプトマネージャーを起動しましょう。

  1. スクリプトを書きたいアプリケーションを起動します。なんでも良いのですが、既にJAWSにそのアプリケーション用のスクリプトが含まれていないものが良いでしょう。なお、メモ帳には初期スクリプトが入っています。いつでも元に戻せるので、メモ帳を使ってみましょう。
  2. JAWSKey+F2キーを押します。[JAWSマネージャー]が起動します。
  3. このアプリケーション(メモ帳)に対する様々なツールを起動することができます。上下矢印キーを使って、[スクリプトマネージャー]を選んで実行します。
  4. メモ帳用のスクリプトが表示されます。既にいろいろ書いてあると思います。Ctrl+Aを押して全選択、Delキーを押して削除してしまいましょう。

初めてのスクリプト

それでは、スクリプトを書いてみます。伝統的に初めてプログラミング言語を習得するときに出題される課題は、「画面に"Hello World!"と表示する」というものです。今回もこれに習って、"hello JAWS Script World!"としゃべらせてみましょう。

スクリプトのひな形を生成する

下記のようにして、スクリプトの枠組みだけを生成します。

  1. 新しいスクリプトを挿入したい場所にキャレットを置きます。今はまだスクリプトは一つもありませんから、ファイルの先頭にキャレットはあることでしょう。
  2. Ctrl+Eキーを押します。[新しいスクリプト]ダイアログが表示されます。
  3. Alt+Nを押して[スクリプト名]のエディットボックスに移動して名前を入力します。 SayHello とでもしましょうか。
  4. Alt+Kを押して[キーに割り当てる]のチェックボックスをオンにします。これにより、ショートカットキーでこのスクリプトが起動できるようになります。
  5. Alt+Aを押して[割り当てるキー]のエディットボックスに移動して、このスクリプト用のショートカットキーを設定します。実際に押すキー操作を行います。Ctrl+Shift+Hキーを押してみましょう。Hは、"Hello"の意味をちょっとだけ込めてみました。
  6. Tabキーを何度か押して[OK]ボタンへ移動しスペースを押して実行します。スクリプトの枠組みだけが生成され、処理を入力できる位置にキャレットが置かれます。
Script SayHello ()

EndScript 

ScriptとEndScriptの間に処理を書いていきます。それでは下の1行を書いてみましょう。

SayString("Hello JAWS Script World!")

それでは、書いたスクリプトを保存します。Ctrl+Sキーを押します。「コンパイル完了」という音声が聞こえるでしょうか。エラーが出た場合にはなにかのタイプミスや編集ミスがあります。エラーの指示に従って修正します。

それでは、メモ帳に戻ってCtrl+Shift+Hを押してみましょう。Hello Worldは聞こえましたか?

次のステップ

それでは、今度はもう少し実用的なスクリプトを書いてみましょう。

ここで取り上げるのはTeraPadというエディターソフトです。特別な音声ソフトへの対応をしているわけではないため、現状JAWSでなければ使えないソフトの一つでしょう。

TeraPadには、行数と桁数だけを読み上げるコマンドはありません。しかし、文章の編集を行っているとき、行数と桁数は気になる情報の一つです。このコマンドを追加してみましょう。

アプローチを検討する

さて、どのようにしたらこのコマンドは実現できるでしょうか。ちょっと考えてみましょう。

  • 画面上にそれらしいことは書かれていないか
  • どんな手順で読み上げれば良いか

考えることはこの二つです。テキストでは、スクリプトの1例を示すだけにします。

まず、JAWSカーソルを使って画面上を探索します。それらしいことは画面のどこに書かれているのか、どんな形式になっているのかを探し出します。続いて、これを機械的に行うための順番を検討します。下記がプログラム例です。

Script SayLineAndColumn ()
var
String sLine
SpeechOff()
InvisibleCursor()
RouteInvisibleToPc()
JAWSBottomOfFile()
JAWSHome()
NextWord()
let sLine = GetWord()
NextWord()
let sLine = sLine + GetWord()
SpeechOn()
SayString(sLine)
PCCursor()
EndScript 

変数を使う

プログラミング言語では、一時的に情報を覚えておく箱(のようなもの)を「変数」と呼びます。上記の例ではsLineがこれに当たります。sLineは最終的に読み上げる内容を覚えておく変数になっています。

JAWSスクリプトでは、変数を各スクリプトの先頭で定義します。定義方法は

var
[変数の型] [変数名]
...

となります。変数はカンマで区切っていくつも定義することができます。

変数には、覚えておく情報によって型があります。文字列を覚えておくのか、数値を覚えておくのか、他の情報を覚えておくのか。使用頻度の高いものを上げます。

  • int: 数値(あるいは1文字)を格納します。
  • string: 文字列を格納します。
  • handle: ウィンドーハンドル(後述)を格納します

変数への代入

算数や数学ではイコール記号は、その左側(左辺)と右側(右辺)が等しいことを表します。しかし、コンピュータ言語でのイコール記号は、右辺を左辺に代入(結果をコピー)することを表します。例えば

a = b

は、Bの内容をAにコピーすることを意味します。

JAWSスクリプトではもう少し特殊な書き方をします。

let a = b

関数

スクリプトと似たものに「関数」があります。数学で言う関数とほぼ同じですが、関数内部でいろいろな処理が行われます。また、結果が返ってこないものもあります。

SayString("Hello")

も関数でした。 各関数の動作や使い方は、JAWSのスクリプトマニュアルを参考にすることになります。このテキスト内でも、使用頻度の高いものについては解説をしておきます。

関数はなにかしらの結果を返します。基本的な呼び出し方法は

let a = f()

のようになります。fという関数が返してくるなにかをaという変数に代入しています。

let sLine = GetWord()

は、カーソル位置にある単語を取得して返してくれるGetWord関数の結果をsLineという変数に代入している、ということになります。

代入を行わない例は

SayString("Hello")

のようなもので す。

各関数の働き

上記例で使用した関数の働きを解説します。

  • SayString : 指定した文字列を読み上げ
  • SpeechOff : 音声を停止
  • SpeechOn : 音声開始
  • PCCursor : PCカーソルに切り替える
  • InvisibleCursor : レビューカーソルに切り替える
  • JAWSBottomOfFile : 画面の一番下に移動
  • JAWSHome : 画面左端に移動
  • NextWord : 次の単語に移動
  • GetWord : カーソル位置の単語を取得

その他

上記例を参考にして、いろいろなスクリプトを書くことができるでしょう。アレンジしてみましょう。

イベント関数を作ってみよう

イベント関数は、JAWSが自動的に呼び出す名前の決まった関数です。この関数を時分で作る事により、JAWSの振る舞いを変更することができます。例えば、アプリケーションがAlt+Tabキーなどで切り替えられたりアプリケーションを起動したときに呼び出されるAutoStartEvent()関数を書いておけば、Alt+Tabキーを押したときの動作を変更できます。 先ほどのTeraPadのスクリプトを使って、TeraPadを起動したりAlt+TabキーでTeraPadに切り替えたときに、行数桁数を読み上げるようにしてみましょう。

アプローチを検討する

せっかく前に書いたスクリプトがあります。これを最大限利用して、どうすれば目的を達成できるかを考えてみましょう。満たさなければならないことは

  • 関数名はAutoStartEventであること
  • まず最初に、行わせたい追加処理を書く
  • 元々存在していた初期スクリプトのAutoStartEvent()を呼び出して終了する。

です。それでは一例を示します。

Function autostartevent() PerformScript SayLineAndColumn() AutoStartEvent() EndFunction

スクリプト内から別のスクリプトの実行

スクリプト内から別のスクリプト(違うファイルに含まれているスクリプトでもよい)を実行することができます。関数の呼び出しでは関数名だけを書けばよかったのですが、スクリプト実行では

performScript スクリプト名

のように書きます。"SayLineAndColumn"を実行している部分がこれに当たります。

条件分岐

もし、行数や桁数が画面上に書かれていなかった場合のことを考えてみましょう。TeraPadのメニューバーから[表示]→[ステータスバー]のチェックを外してみるとこの状態になります。先ほどのスクリプトをAlt+Delキーを押して実行してみましょう。正常に動作するでしょうか?

プログラミングを行う場合、正常に動作する環境でのみテストを行うと、思わぬ落とし穴に遭遇することがあります。考えられる多くの状況に対応し、エラーも含めて対処する必要があります。

アプローチを検討する

さて、どうしたらこの問題に対処できるでしょうか。以下のように動くスクリプトを考えます。

  • 行数桁数が表示されていたらそれを読み上げる
  • 行数桁数が表示されていなかったら「ステータスバーが表示されていません。メニューバーの表示メニューにあるステータスバーのチェックを入れてください」のように、メッセージを読み上げる

問題は「行数桁数が表示されているかどうかを判定する」方法です。少し考えてみましょう。テキストではその1例をスクリプトだけ示します。

Script SayLineAndColumn()
var
String sLine
SpeechOff()
InvisibleCursor()
JAWSBottomOfFile()
JAWSHome()
NextWord()
let sLine = GetWord()
if StringContainsChars (sLine, "行") then
NextWord()
let sLine = sLine + GetWord()
SpeechOn()
SayString(sLine)
PCCursor()
Else
SpeechOn()
SayString("ステータスバーが表示されていません")
PCCursor()
Endif
EndScript 

条件による処理の分岐

プログラミング言語には条件により処理を分岐して実行する機能があります。プログラミング言語に限らず実生活でもこのような分岐は至る所で行っているはずです。「もしも、電車に乗るまでにちょっとあるならば、売店でお茶を買おう、そうでなければ急いで電車に乗る」は立派な条件分岐行動だと言えるでしょう。

JAWSスクリプトでは下のように書きます。 if (条件式) then 処理部分 elif (条件式) then 次の処理 else どれでもなかったときの処理 Endif

新しく使った関数

紹介したスクリプト内で新しく使った関数を解説します。

  • StringContainsChars : 文字列内に、ある文字が含まれているかどうかをチェックする。含まれていれば1(true)が返され、含まれていなければ0(false)が返される

JAWSスクリプトの基本的文法

これまでに小さなスクリプトを書いてきましたが、改めて文法の基本的事項を纏めてみたいと思います。

基本事項

原則として、以下のような規則があります。

大文字と小文字は区別しません。習慣的に大文字と小文字を混在して書きますが、実際にはすべて大文字のものもすべて小文字のものも同じものとして扱われます。

saystring("Hello World")
SayString("Hello World")

上の2行は同じ意味になります。また、

let a = b
let A = B

も同じ意味になります。

行内コメントは ';'で始まります。セミコロンより後ろに書いたものはコメントとして扱われます。

let a = b ; ここからコメントです。bをaに代入しています。

'/'と'/'で囲まれた部分はコメントとなります。複数行にコメントを書くときなどに用います。 /* ここから数行はコメントです。

let a = b
SayString("Hello World")
まだまだコメントです。
*/

改行、空白は基本的に自由に挿入できます。極端に言えば1行で書くこともできます。

Script foo() SayString("Hello World") EndScript

と書いても、

Script foo()
SayString("Hello World")
EndScript

と書いても文法的には間違いではありません。読みやすくわかりやすく書くことが大切です。適宜改行して、適宜インデント(字下げ)を行うことで、スクリプトは読みやすくなります。

スクリプト

キーワード "Script"で始まり、キーワード"EndScript"で終了します。この間に一連の処理を記述します。スクリプトは値を返すことはできません。また、引数を受け取ることも基本的にはできません。

Script foo()
var
int a
let a = 3
SayInteger(a*5)
EndScript

関数

キーワード"Function"で始まり"EndFunction"で終わります。関数はスクリプトと違い値を返すことができます。また、複数の引数を受け取ることができます。関数は、スクリプトと違い、ショートカットキーを設定して呼び出すことはできません。なんらかのスクリプトの中から呼び出さなければなりません。

Function bar()
var int a
let a = 5
return a*a
EndFunction

引数を使う場合には下のように記述します。スクリプトマネージャーでパラメータを設定して関数のひな形を生成すると下のようになるでしょう。下の例は与えられた二つの数を掛け合わせてその答えを返しています。 Function bar(int x, int y) var int a let a = x * y return a EndFunction


### 変数定義

スクリプトや関数内で定義された変数は、そのスクリプトや関数内だけで有向なローカル変数です。また、それらの呼び出しの度に値は初期化されます。C言語で言うところの自動変数です。スタティックなローカル変数を作る事はできません。

変数定義の基本形は下のようになります。

var int a


また、いくつかの変数をカンマで区切って定義することもできます。

var int x, int y, string s, handle h, object o


### グローバル変数

グローバル変数は、その変数を定義したスクリプトファイル以外からも参照・書き換えが可能な変数で、一度定義されると、JAWSが終了するまでなくなることはありません。

一般的にはスクリプトファイルの先頭に近い場所で、すべてのスクリプトや関数の外側で行わなければなりません。C言語で言うならexternに近い振る舞いをします。下のように定義します。基本的には変数定義とほとんど変わりません。

Globals int g_a, String g_s, Handle g_h


### 定数

定数は下のように定義します。変数と形は似ていますし、使い方も似ています。違いは値が変えられないことです。プログラムを読んだり書いたりしているときに、繰り返し使われる意味のある数に、名前を付けることによってプログラムを読みやすく書きやすくする効果があります。複数回使うことがわかったら、積極的に定数にするのがよいでしょう。

定数はどこでも定義することができますが、一般的にはグローバル変数と同じように、ファイルの先頭付近、すべてのスクリプトや関数が始まる前に定義するのがよいでしょう。

const true=1, false=0, ignore=-1, sMessage="Hello World"


定数を使った場合、下のようにSayString()を書くことができます。

SayString(cMessage)

日本語以外の言語に自作のJAWSスクリプトを対応させたいと思ったとき、このように文字列を定数にして纏めておくと、その部分だけを翻訳することによって多くの言語に対応することが可能になります。実際にこの手法は多くのスクリプトで使われており、国際化するときに最初に行う作業になります。

文字列

定数の所でもサンプルを示しましたが、文字列を定数として扱う方法を纏めておきます。基本的に文字列はダブルコーテーションで囲んで書きます。これまでにもSayString()関数の引数として使ってきましたので、なじみの形だと思います。

"これは文字列です。"

文字列を定数にすると以下のようになります。

const
cMessage="これは定数です。"

特殊なものとして、複数行の文字列を纏めて扱いたいことがあります。例えば、ソフトウェアの使い方のような比較的長い文章を読み上げさせたり、あるいは仮装バッファに表示させたりするときに使用されます。基本形は下のようになります。

Messages
@cMessage
ここからしばらくメッセージです。
スクリプト開発はなかなか楽しい作業です。
まだまだメッセージです。
終わりましょう。
@@EndMessages 
アットマークで始まり、続いて定数名(メッセージに付ける名前)を書きます。終了は、アットマークを二つ並べて書きます。上記例では一つだけメッセージを作りましたが、MessagesとEndMessagesの間にいくつでもメッセージを作る事ができます。同じメッセージを何度も使用する場合には一度このようにして纏めておくことはとても重要です。

### 別のスクリプトファイルのインクルード

スクリプト内から別のファイルを挿入することができます。これまでに紹介してきた定数やグローバル変数・メッセージなどを纏めておくと、国際化するときに作業が楽になるだけでなく、同じファイルを別のスクリプトでも使うことができます。

習慣的に、定数やグローバル変数を集めたファイルの拡張子を.jsh、メッセージを集めたファイルの拡張子を.jsmとすることが多いようです。

ファイルの読み込みは、どの場所でも行うことができます。読み込んだ結果がスクリプトの文法上正しければ良いのですが、一般的にはスクリプトファイルの先頭付近で読み込まれることがほとんどです。なお、C言語のようにマクロ展開、プリプロセッサーを使った高度な処理はできません。

Include "foo.jsh" Include "bar.jsm" ;; この下にスクリプト・関す本体を書きます。


### 関数呼び出し

これまでにも関数呼び出しはたくさん書いてきましたが、改めて纏めておきましょう。関数の呼び出しは、スクリプト内・関数内で行うことができます。

function foo() SayString("Hello World") EndFunction


ここでSayStringの行が関数呼び出しです。

関数呼び出しにおいては以下のような決まり(制約)があります。

* 呼び出される関数で指定されている引数は必ず渡さなければならない
* 引数の数と型は一致していなければならない
* 呼び出される関数でOptionalと設定されている引数は渡さなくても良い

SayString()関数は、必ず一つ文字列を引数として渡さなければなりません。他の型の変数や定数を渡すとエラーになります。

### スクリプト内からのスクリプト実行

Tabキーや矢印キーなど、もっとも基本的なキーに対してスクリプトを書きたくなることがあります。これらのキーはデフォルトのスクリプトが用意されており、それが動作しないとJAWSが正常に動作しないことになります。自前の処理がすべて終了し(あるいは自前ではやることがない場合)、適切にこのデフォルトスクリプトを実行しなければならないことがあります。このようなときに、スクリプト内から別のスクリプトを実行することができます。これを忘れてしまったことによるバグは見つけにくく、また致命的になることもあります。

下の例では、Tabキーに割り当てられたTab()スクリプト内から、デフォルトのTab()スクリプトを実行しています。

Script Tab()
var
int iFlag ;; この変数が0であればデフォルトスクリプトを実行
if iFlag then
SayString("自分のスクリプト")
Else
PerformScript Tab()
Endif
EndScript 

### 条件分岐

条件分岐はプログラムを行うときに避けては通れない考え方の一つです。JAWSスクリプトでは下のように書きます。

if (条件式) then 処理部分 elif (条件式) then 次の処理 else どれでもなかったときの処理 Endif


条件式は、この後で解説する演算子を使って複数の式を使って書くこともできます。また、if文の中にif文を入れてもかまいません。

### 演算子と優先順位

特に条件式を書くときに、いくつかの条件を接続したり条件を反転させたりすることができます。また、演算子には優先順位があり、優先順位の高いものから先に評価されます。まず、優先順位を高い方から示します。

1. 第1優先順位 
o ! : ではない
o ( ) : 式を囲んで一つの式とする
o == : 等しい
o < : より小さい
o > : より大きい
o != : 等しくない

2. 第2優先順位 
o && : 両方が成り立つ

3. 第3優先順位 
o || : どちらかが成り立つ

式は上に上げた順番に従って評価、結合されます。例えば、
a == b
は、aとbが等しいことを表します。
(a == b) || (c == d)
は、aとbが等しいかcとdが等しいかのどちらかが成り立ったときに条件が成立します。
また、 (a != b) || (c != d && e == f)
は、aとbが等しくないかあるいは、cとdが等しくなくてeとfが等しいときに条件が成立する、と言う意味になります。

プログラミングを行う上でどのような条件になったときにどのような処理を行うかを考えることは、とても難しくまたとても大事なことです。順番を間違えるとプログラムは正しく動作しないばかりではなく、一見正しいように見えても細部までテストを行ってみると正しく動作していないことがあります。

### 繰り返し

一般的に繰り返し処理には二つのタイプがあります。

* ある条件が成り立っている間特定の処理を繰り返す場合
* ある決まった回数特定の処理を繰り返す場合

前者のような場合には下のように書きます。

while (条件式) 繰り返し処理 endwhile


例えば、カーソルを1文字ずつ動かしながら、'z'という文字が見つかったらそこで停止するような場合は、

while (GetCharacter() != "z") NextCharacter() EndWhile


となります。後者の場合は、

For カウンター変数 = 初期値 to 最大値 繰り返し処理 EndFOr


となります。

### 纏めに

JAWSスクリプトの文法は、他の言語に比べて圧倒的に機能がありません。そのため、他の言語ならば簡単に書けることでも少し遠回りをしなければならないことがあります。繰り返し制御はその良い例です。しかし、その反面略記法があまりなく、キーワードも少ないため、覚えやすいとも言えます。ぜひいろいろなスクリプトを書いてみてください。

## 文字列処理

一般的にプログラミングを行うとき、文字列処理は避けて通ることはできません。文字列から必用な部分を切りだしたり、文字列内に特定の文字が含まれているかどうかを検査したり、文字列を連結したりなどの様々な処理を行います。

### 変数への文字列代入

文字列を扱うことができる変数の型はString型です。この変数に文字列を代入するのは、変数に数値を代入するのと同じです。

let sHello = "こんにちは、JAWSスクリプトの世界へようこそ!"


それでは、この変数を音声出力させてみましょう。

SayString(sHello)


### 文字列連結

二つの文字列を一つに連結することができます。プラス記号を使って二つの文字列を連結すると、一つの文字列として扱われます。

let sHello = "こんにちは、" + "JAWSスクリプトの世界へようこそ!"


実用的な例を示します。

let sName="鈴木" SayString(sName + "様")


このようにすると、上のsNameの値を変更すると、名前に「様」を付けて読み上げさせることが楽にできます。

### 文字列の比較

二つの文字列が同じであるかどうかを検査することができます。

let sWin = GetWindowName(GetCurrentWindow()) if sWin == "設定" then SayString("設定ウィンドーにいます。") Endif


上記例は、sWinの内容によって処理を分けるスクリプトの一部です。このように ==演算子を使って左辺と右辺の文字列が同じであるかどうかをチェックできます。

### 文字列検索

ある文字列の中に、特定の文字列が含まれているかどうかをチェックします。例えば、画面最下行の内容の中に「挿入」という文字列が含まれていれば挿入モード、と言ったような使い方が考えられます。文字列検索の基本的例を示します。

var String sLine let sLine = "これは、画面最下行の内容です、現在挿入モードで動作しています" ;本来は画面上から文字列を取得します if StringContains(sLine ,"挿入") then SayString("挿入モードです、上書きモードに切り替えるにはInsertキーを押してください。") Endif ```

StringContains関数の、最初の引数が、検査される文字列、2番目の引数が検索する文字列です。 これと似た関数で、文字列ではなく文字が含まれているかどうかをチェックすることができる関数があります。例えば文字列が日付のようなものであるかを簡単に調べるには、こんなコードが書けるかもしれません。

let sDate = "平成25年8月26日(月)"
if StringContainsChars (sDate, "1234567890")
&& StringContainsChars (sDate, "年月日") then
SayString(sDate + "は日付です。")
Endif

StringContainsChars関数は、1番目に指定した文字列の中に、2番目に指定した文字列中のどれかの文字が含まれていれば真となる関数です。つまり、

StringContainsChars (sDate, "1234567890")

は、sDateの中に1~0のどれかの文字が含まれていればよい、ということになります。上記例では、数字と「年・月・日」のどれかが含まれていれば日付のようだ、と見なしています。「今日」はだめでも「10日」はOKとなります。

文字列置換

ある文字列を、別の文字列に置き換えることができます。簡単な例を示します。

let s = "こんにちは。私はJAWSスクリプトの勉強をしています。"
let s = StringReplaceSubstrings (s, "こんにちは", "こんばんは")
SayString(s)

これは、sの中にある「こんにちは」を「こんばんは」に置換しています。これと時刻判定を組み合わせると、簡単な挨拶を読み上げるスクリプトが書けるでしょう。

上記関数と似た関数で、文字列置換ではなく文字置換の関数があります。全角スペースをすべて半角スペースに置換する例を示します。

let s = " 先頭のスペースは全角です。"
let s = StringReplaceChars (s, " ", " ")
SayString(s)

この関数は、1番目の引数の中に2番目の引数で指定された文字を見つけると3番目の引数で指定された文字に置換します。

文字列の加工

この他にもJAWSスクリプトには様々な文字列加工関数があります。簡単に紹介します。

  • 大文字小文字変換: StringLower(), StringUpper()
  • 空白削除: StringStripAllBlanks()
  • 先頭・最後の空白を削除: StringTrimLeadingBlanks() StringTrimTrailingBlanks()
  • 文字列の反転: StringReverse()
  • 文字列から左(右)の指定文字数を得る: StringLeft() StringRight()
  • 文字列の左(右)側指定文字数を削除する: StringChopLeft() StringChopRight()

これらの関数を使って、文字列を加工して音声出力したり、あるいは状況判断のために使用します。

実用的スクリプトの開発

これまでは、画面に書かれている情報を使ったスクリプトを書いてきました。読み上げの情報源としては2番目に紹介しているものです。実際に見えているものを使ったスクリプトなので、最も簡単に習得が可能ですが、実用的安定性を考えた場合には、アプリケーションやOSの内部情報を用いた方が効果がある場合があります。事実、最近製作されているスクリプトの多くが内部情報を利用しています。実際に画面に見えている方法ではないため、アプリケーションを専用のツールで解析し、その情報を元にスクリプトを記述します。経験と慣れが必要な領域かもしれません。

WindowsはなぜWindowsと言うのか?

ところで、なぜWindowsはWindowsという名前なのでしょう。ウィンドー(窓)の複数形?なにか他の意味があるの?

Windowsの画面を思い出してみましょう。デスクトップがあり、アプリケーションのウィンドーが重なり合って表示され、その中にダイアログウィンドーが表示されます。デスクトップを基点にした木構造になっているのです。アプリケーションの親はデスクトップ、あるダイアログの親はアプリケーション、例えばOKボタンの親はダイアログ、と言ったような関係を持っています。 Windowsプログラミングの世界では、実際に窓上の表示をしていなくてもあらゆるパーツをWindowと読んでいます。ボタンもチェックボックスも、あるいは書かれている文字の集合(テキストラベルとも言います)もすべてWindowです。このWindowに関する情報を使って、JAWSスクリプトを書いていくことができます。

Windowには、下記のような情報が含まれています。このどれかを使って、情報を取得します。

  • ウィンドーの名前
  • ウィンドーの種類
  • コントロールID
  • ウィンドークラス
  • ウィンドーハンドル
  • ... その他 ...

基本的にはウィンドーハンドルだけがアプリケーションを起動するごとに変化します。アプリケーションの作りによってはコントロールIDも変化してしまうものがあります。

ステータスバーの表示判定

前回、行数や桁数の表示があるかどうかを判定する方法として、実際に書かれている文字を探して、その内容で判定しました。もちろん、この方法でも良いのですが、ステータスバーというWindowがあるかどうかでも実現することができます。この方が安定して動作するかもしれません。

TeraPadのウィンドー構成

TeraPadは、アプリケーションウィンドーの子供として下記のウィンドーがあります。論理的順番も下のようになっています。

  1. 設定ファイル格納用
  2. TeraPadのプログラム名
  3. エディットウィンドー(用途不明)
  4. ツールバー
  5. 編集文書本体
  6. ステータスバー

厳しいエラーチェックが必用なスクリプトでは、厳密にステータスバーを探し当てる必用がありますが、個人で使う場合には、エディタを使っていると言うことは文書本体にPCカーソルがあるだろうことが予測できますので、その次がステータスバー、ということになります。

簡単に例を示します。もっとアレンジしてみましょう。

Script SayLineAndColumn()
var
String sLine,
Handle hStatusBar
;; ステータスバーのウィンドーハンドルを保存
let hStatusBar = GetNextWindow(GetFocus())
if GetWindowType (hStatusBar) == "ステータスバー" then
;; ステータスバーが見つかった
SpeechOff()
InvisibleCursor()
RouteInvisibleToPc()
JAWSBottomOfFile()
JAWSHome()
NextWord()
let sLine = GetWord()
NextWord()
let sLine = sLine + GetWord()
SpeechOn()
SayString(sLine)
PCCursor()
Else
;; ステータスバーは見つからなかった
SpeechOn()
SayString("ステータスバーが表示されていません")
Endif
EndScript 

各関数の働き

上記例で使用した関数の働きを解説します。

  • GetFocus : 現在フォーカスがある(この場合はPCカーソルがある)ウィンドーハンドルを取得する
  • GetLastWindow : 同じレベルの最後のウィンドーハンドルを取得する
  • GetWindowType : ウィンドーの種類名を取得する

スクリプトを書くために考えるべき事

さて、これまでスクリプトを書いてきましたが、音声で使いやすいスクリプトを書くために考えなければならないこと、どのような機能を盛り込めば便利なスクリプトになるかを考えてみましょう。

画面を見ながら使うことを前提にして作られているソフトウェアは必ずしもスクリーンリーダーで使ったときに使いやすいとは限らないものです。なぜそのようになってしまうのか?実際に目的のソフトウェアを注意深く使いながら、じっくり観察することが大切です。

操作の音声フィードバック

最も基本的な音声フィードバックとして、なんらかの操作を行った時に、その操作を行ったと言うことを確実にユーザにフィードバックしなければなりません。キーを押したときに発生するキー読み上げはその最も基本的な機能の一つです。

例えば、エディタのようなソフトを考えてみましょう。あるショートカットキーを押すと編集中の1行が削除されるとします。そのキーを押したときに「行削除」と読み上げることにより、ユーザは自分が今行ったことを確実に知ることができます。

スクリプトの初歩として、ショートカットキーを押したときになんらかのフィードバックを読み上げるようにすることを目指すことはとても重要です。

結果の音声フィードバック

次に必要なのは、操作の結果を音声でフィードバックすることです。何らかの操作を行ったときにはその結果があるはずです。例えば、文字を削除したのならば指定した文字が消えてその場所に新しい文字が入ったことでしょう。その結果を正しく伝えなければ、音声での操作は困難になります。エディタで矢印キーを押した場合には、キャレットが移動します。音声ではその結果として、移動先の文字を正しく読み上げてユーザにフィードバックしなければなりません。効率的なフィードバックにより、劇的に使いやすくなることもあります。

先ほどの行削除の例では、行削除の結果新しく上に上がってきた行を読み上げることで、新しい行も不要なのかあるいはもう削除する必要がないのかを瞬時に判断することができるでしょう。

状態の音声フィードバック

コンピュータが今どのような状態なのかを適切にユーザにフィードバックすることは、ユーザの混乱を最小限に食い止める効果があります。例えば、検索操作を行ったとき、検索に時間がかかったとしましょう。画面を見れば今どのような状態なのかは一目でわかるでしょう。 しかし、適切なフィードバックがなければ検索操作に失敗したのか、あるいは時間がかかっているのかを判断できず、また検索操作を繰り返していわゆる迷子になってしまうこともあります。

その他必要な音声フィードバック

実際にスクリプトを書いてくと、上記に上げたフィードバックの他にも状況によって様々な情報を音声で伝える必要が出てきます。例えば、文字数制限のある入力を行っているような場合には、残りが何文字あるのかを瞬時に知る手段が必要でしょう。しかし、1文字入力する度に残り文字数を読み上げてしまっては入力の妨げになります。適切なショートカットキーを作って、残りの文字数を読み上げたり、入力が一定時間停止したりしたならば残り文字数を読み上げると言ったアイディアが考えられます。

このほかにも、JAWSスクリプトでは音声ではなくサウンドファイルを再生することができます。音声でのフィードバックが難しいような場合でも短いサウンドファイルを再生することによって状態を伝えるようなことも考えられます。

応用例

それでは、実際のスクリプト例を紹介しましょう。どのようなコンセプトでこれらのスクリプトを書かれているか、肝になっている部分はどこになるのかなどを紹介します。

Twitterクライアントのスクリプト例

これまでにも多くのTwitterクライアントが開発されてきました。スクリーンリーダーでも十分使えるものも多くあります。その中から、フリーソフトとして公開されているTween用のJAWSスクリプトを紹介します。

コンセプト

このソフトは大変機能が多く、Twitterを楽しむ上で必要な機能はほとんど入っています。その中で日々使うために必用な読み上げ機能をいくつか盛り込んでいます。

  • 書き込み時に発生する不要な読み上げの抑制
  • Tab, Shift+Tab, Ctrl+Tabキーなどのよく使われるショートカットキー操作時に、操作に応じた最適な読み上げを行うように調整

サンプルコード

;; JAWS script for Tween

Include "hjconst.jsh"
Include "common.jsm"

const
csTweenList="CList72665",
csTweetInputArea="ツイート入力"

Void Function FocusChangedEvent (handle FocusWindow, handle PrevWindow)
var
String sFocusIdString

let sFocusIDString = GetControlIDString(FocusWindow)
;; ツイート入力欄に文字を書く度にこのイベントが呼ばれる。
;; そのたびに残り文字数を音声出力してしまうので、フォーカス移動がないと判断できるときは、すべての処理をスキップする。
if sFocusIdString == "StatusText" 
&& FocusWindow == PrevWindow then
return 0
Endif
;; Ctrl+Tab, Shift+Tabキーでのタブ切り替えを読み上げる
if GetControlIDString(FocusWindow) == csTweenList then
SayMessage(OT_CONTROL_NAME, GetWindowName(GetParent(FocusWindow)))
SayObjectTypeAndText (0)
return
Endif
;; 発言入力欄に入ったときに発言入力欄であることを通知
if sFocusIdString == "StatusText" 
&& FocusWindow != PrevWindow then
SayMessage(OT_CONTROL_NAME, csTweetInputArea)
SayObjectTypeAndText (0)
return
Endif
Default::FocusChangedEvent(FocusWindow, PrevWindow)
EndFunction

解説

JAWSは、ユーザの操作に応じて自動的に関数を呼び出すことがあります。前の章で記述したAutoStartEvent()などがそれです。このスクリプトでは、フォーカスが変化したときに呼び出されるFocusChangedEvent()関数をカスタマイズすることによって、コンセプトで上げた二つの機能を実現しています。本来ならこの関数を直接変更することは推奨されませんが、アプリケーションの設計によって、このようにしなければなりませんでした。

まず、このイベントが呼び出されたときに行っているのは、ツイート入力部分で文字入力を行っているだけではないかというチェックです。今のフォーカス位置はツイート入力エリアであり、直前のフォーカス位置と今のフォーカス位置が同じならば、おそらく文字入力を行っているのだと判断しています。

続いて行っているのがCtrl+Tabキーによる切り替えです。Tweenではいろいろな情報をこのキーで切り替えて読みますが、切り替え時にどの情報に切り替わったかを読み上げてくれないことがあります。どの情報に切り替わったかは、現在のフォーカスウィンドーの親ウィンドー名を参照して音声出力しています。

最後に行っているのが、ツイート入力欄エリアにフォーカスが移ったときに、そのことを音声出力しています。現在のフォーカスがツイート入力エリアであり、現在のフォーカス位置と直前のフォーカス位置が違っているときに、フォーカスはツイート入力エリアに移動してきたのだと判断しています。

GetControlIdString()関数について

.NETアプリケーションでは、ウィンドーの情報を得るためにGetControlIdString()関数を使うことができます。ほとんどの場合一意な文字列を返してくれるので、ウィンドー名やウィンドークラス名で状況を特定できないような場合でも、この関数を使うことによってフォーカス位置などの特定が可能になります。このスクリプトではツイート入力欄の識別やツイート一覧の識別に使っています。

HTMLエディタのスクリプト例

HTMLを書くためのエディタは数多くあります。その中で、この資料を書くためにも使ったCresent Eveというエディタのスクリプトを紹介します。

コンセプト

このエディタの特徴は、リアルタイムにHTML文法チェックを行ってくれることにあります。HTML文法を正しく理解し、完全に手で入力することはかなり難しくなります。このエディタでは、その局面で利用可能な要素はどれなのか、その要素に許されている属性はどれなのかを表示してくれ、そのなかから矢印キーで選べるようになっています。このスクリプトでは、この入力支援部分に着目して読み上げ機能を実装しています。

サンプルコード

Include "hjconst.jsh"

globals
Handle hComposition

;; ウィンドーが生成されたときに、そのウィンドーが候補ウィンドーであるかどうかをチェック。

Void Function WindowCreatedEvent (handle hWindow, int nLeft, int nTop, int nRight, int nBottom)
if GetWindowClass(GetParent(hWindow)) == "ForecastWindow"
&& GetWindowClass(hWindow) == "ListBox" then
SayMessage(OT_MESSAGE, "選択オープン")
let hComposition = hWindow
return
Endif
Default::WindowCreatedEvent (hWindow, nLeft, nTop, nRight, nBottom)
EndFunction

;; 候補ウィンドーが消えたときに、そのことを音声通知
Function WindowDestroyedEvent (handle hWindow)
if hWindow == hComposition then
SayMessage(OT_Message, "選択クローズ")
Endif
default::WindowDestroyedEvent (hWindow)
EndFunction

解説

このスクリプトもイベントを活用したものです。ウィンドーが生成されるときに呼び出されるイベントと、ウィンドーが生滅するときに呼び出されるイベントをカスタマイズしています。

ウィンドーが生成されると、そのウィンドーがタグの選択ウィンドーであるかどうかをチェックしています。もしタグ選択ウィンドーであれば、選択ウィンドーが開いたことを音声出力し、そのウィンドーハンドルをグローバル変数に保存しています。

続いて、ウィンドーが生滅するときに呼び出される関数をつかって、選択ウィンドーが消えたことを音声出力しています。判定は生成されるときに保存したウィンドーハンドルと同じものが生滅したときに、選択ウィンドーが消えたのだとしています。

追記

現状のJAWS14では、上記スクリプトが正しく動作しないことがあります。システムの状況により、上記イベントが期待したように呼び出されないことがあるからです。

最後に

例えばWord, Excelのような大きなソフトウェア用のスクリプトを開発するには何年もの時間がかかります。開発に必用な技術もかなり高いものが要求されます。しかし、今回取り上げたTeraPadのような簡単なアプリケーションの場合には、ちょっと便利にするような改良は慣れてしまえば15分もあればできてしまいます。

これまでは、音声対応はアプリケーション制作側でしか行うことができず、結果としてなかなか思うようにならないことが多くありました。JAWSスクリプトを使用することで、使用者が積極的にアプリケーションの音声対応を行うことができるようになります。

インターネットからダウンロードしたソフトで、ほんのちょっと便利になればと思ったとき、JAWSスクリプトはその力を発揮するだろうと思います。まずは実用にならないかもしれないものから、次はちょっと実用になるかもしれないものに、ゆっくり確実にステップアップを目指していただければと思います。

48