タイムラインウィンドウはパッチャーウィンドウと同様な構造体を持っています。Timeline オブジェクトは Event と呼ばれるデータ構造体の連結リストを持っています。これは、パッチャー内の Box に似たものです。Event はタイムライン内のデータ(時間タグを持ったメッセージ)、およびそのデータを編集し、表示することのできるオブジェクトへの参照の2つを保持しています。個々の Event は Editor と呼ばれる特殊な形式の Max オブジェクトのインスタンスのヘッダです。これはちょうど個々の Box がユーザインターフェイスオブジェクトのヘッダであるのと同じです。実際、Event は t_box 構造体からいくつかのフィールドを借りています。これによって、特定のパッチャーウィンドウルーチンが Timeline オブジェクトの中で使用されるようになります。パッチャーのユーザインターフェイスオブジェクトで使われているものと同様な多くのコンセプトが、タイムラインのためにエディタを書く際にも適用されます。しかし、同じエクスターナルオブジェクトの中での2つの役割を結びつけることはやや面倒です。(そして、それを行うための正しいテクニックは、ここでは言及しません)
エディタを書くためには、事実上、基本的な3つのステップがあります。始めに、初期化ルーチンで1つまたはそれ以上のデータ型にエディタを登録するプロセスがあります。次に、あなたのオブジェクトの新しいインスタンスが生成されたときに、Event 構造体を初期化しなければなりません。そして最後に、タイムラインエディタにとって必須の、あるいはオブションのメッセージをサポートするためのメッセージを書かなければなりません。これらのメッセージは、描画、ユーザとのインタラクション、タイムラインウィンドウにリンクされたアクションでのオブジェクトへのデータの送信、イベントデータのタイムラインファイルへの保存に関するものです。
タイムラインで動作するエディタオブジェクトを作る最初のステップは、editor_register を使って、オブジェクトを編集できる特定のデータ型と結びつけることです。
editor_register |
||
エディタを特定のデータ型のために登録する場合に、editor_register を使います。 | ||
void editor_register (t_symbol *dataType, t_symbol *name method new, method menu, method update); | ||
dataType | エディタが編集し、表示することができるデータの型。あなたはデータ型に対して好きな名前をつけることができますが、オブジェクトがこのメッセージを登録できるアクションにない限り、エディタをタイムラインで使用することはできません。現在、int、float、list、及び一般的な型のメッセージを扱うエディタが存在します。 | |
name | あなたのエディタの名前。これは、同じデータ型に対して複数のエディタが存在する場合、タイムラインファイルの保存や新しいイベントのポップアップメニューの際にエディタを識別するために用いられます。 | |
new | ファイルから新しいイベントを作るためのメソッド。下記のようにインスタンス生成セクションで書かれます。 | |
menu | 新しいイベントポップアップメニューから新しいイベントを作るためのメソッド。下記のようにインスタンス生成セクションで書かれます。 | |
update | イベントを更新するメソッド。下記のようにメッセージセクションで書かれます。 | |
あなたのエディタオブジェクトの初期化ルーチンでは、クラスを初期化するために setup を呼び出した後、editor_register を使って、あなたのエディタの存在を Timeline オブジェクトに知らせます。Timeline は、データ型によってエディタを分類します。このデータ型とは、エディタが時間内の特定の瞬間にオブジェクトに対して送信するメッセージの型を記述したシンボルです。QuickTime ムービーエディタは、movie と呼ばれる型を考案し、これを Timeline のコンテキスト内で動作するように修正された Max の movie オブジェクトと結びつけるために使っています。あなたのエディタは複数のデータ型に対して登録することができ、新しいインスタンスが生成されると、あなたのオブジェクトはインスタンスが要求するデータ型を報告されます。 |
あなたのエディタオブジェクトは Eventデータ構造体(インクルードファイル ext_event.h で宣言されています)。から始まります
typedef struct myEditor { Event m_event; ...rest of your editor fields here } myEditor;
この構造体は、タイムラインウィンドウでのエディタインスタンスの場所を格納していますが、Timelineオブジェクトやあなたのオブジェクトが必要とするそれ以外の情報も持っています。これは、Event の個々のフィールドに関する説明です。
typedef struct event { | ||
t_object e_obj; | ||
struct smallbox *e_box; | 矩形領域を保持する Smallbox (または Box)へのポインタ。 | |
struct event *e_upd; | リストのリンクの更新 (イベントのスプーリングのために内部で使用). | |
Rect *e_rect; | e_box.内の矩形領域へのポインタ | |
struct event *e_next; | タイムライントラック内のイベントをリンクしたリスト | |
void *e_track; | このイベントがあるトラックへのポインタ | |
struct oList *e_assoc; | 関連付けられたオブジェクトのリスト (内部で使用). | |
t_object *e_o; | 統一されたレシーバのケースでのオブジェクトへのポインタ | |
t_symbol *e_label; | イベント配置ポップアップメニュー用に記述されたテキスト | |
long e_start; | イベントの開始時刻(ミリ秒) | |
long e_duration; | イベントの持続時間(ミリ秒) | |
t_symbol *e_dataType; | メッセージのデータ型 | |
t_symbol *e_message; | 送信されるメッセージのセレクタ | |
t_symbol *e_editor; | エディタ名 | |
struct editor *e_edit; | エディタに関する内部情報 | |
void *e_saveThing; | 内部的なテンポラリ変数 | |
void *e_thing; | 内部的な未使用の変数 | |
short e_wantOffset; | エディタを含むトラックが折りたたまれた場合に、イベントの矩形領域の、トラックの最上部からのオフセットを保存します。 |
|
Boolean e_active; | 現在使用されていません。 | |
Boolean e_preview; | 現在使用されていませんが、非0の値でなければなりません。 | |
Boolean e_constantWidth; | このフラグが非0の場合、スケールが変更された際にイベント矩形領域が同じ幅になるように再計算されます。 | |
Boolean e_editable; | イベントをクリックして内容を変更する場合、このフラグは非0になります。 | |
Boolean e_smallbox; | イベントがBox データ構造体ではなく、Smallbox データ構造体(ext_user.hで定義されています))を使用する場合、フラグは非0になります。 | |
} Event; |
あなたのエディタのインスタンス生成関数は、Event のデータ構造体を初期化する役割を果します。これを行なうためには、event_new と event_box を使います。Event のオブジェクト生成関数の最初のいくつかの引数は(規則によって)標準化され、これらの多くは2つの Event 初期化関数に直接渡すことができます。Event のインスタンス生成関数は次のような形でなければなりません。
void *myEditor_new (t_symbol *dataType, short argc, t_atom *argv); | ||
dataType | 生成されるイベントのためのデータ型。あなたのエディタオブジェクトが1つのデータ型のみで登録されている場合、この引数にあまり注意を払う必要はありませんが、登録したシンボルへの直接のポインタではなく、event_new への引数を渡す必要があります。 | |
argc | argv の atom の数 | |
argv | 次のようなデータを含む配列 |
インデックス | 定数 | 説明 |
0 |
ED_TRACK | イベントの親トラックへのポインタ (A_OBJ) |
1 |
ED_MESSAGE | タイムラインに要請された時にオブジェクトへ送信するメッセージを指定するシンボル (A_SYM) |
2 |
ED_START | ミリセカンドで表される、イベントのスタート時刻 (A_LONG) |
3 |
ED_DURATION | ミリセカンドで表される、イベントの持続時間 (A_LONG) |
4 |
ED_TOP | トラックでのイベント領域の最初 (A_LONG) |
5 |
ED_BOTTOM | トラックでのイベント領域の最後 (A_LONG) |
イベントのメッセージデータに含めることによって、あなた自身がユーザ定義したパラメータを(argc が 6 より大きい場合)追加の引数とすることもできます。
event_new |
|||||||||||
Event の初期化を行なうために、event_new を使います。 | |||||||||||
void event_new (Event *evnt, void *track, t_symbol *dataType, t_symbol *message, t_symbol *editor,t_symbol *label, long start, long duration, long flags, t_box *box); | |||||||||||
evnt | 初期化するイベント。あなたのオブジェクトは Event ヘッダから始まるので、これはあなたのオブジェクトへのポインタでなければなりません。 | ||||||||||
track | あなたのインスタンス生成関数によって受け取られた引数 ED_TRACK の a_w.w.obj フィールドを渡します。 | ||||||||||
dataType | あなたのインスタンス生成関数が渡された引数、dataType を渡します。 | ||||||||||
message | あなたのインスタンス生成関数によって受け取られた引数 ED_MESSAGE の a_w.w_sym フィールドを渡します。 | ||||||||||
editor | エディタの名前, editor_register に渡したものと同じシンボル。 | ||||||||||
label | 任意のテキスト; 引数 editor に渡したものと同じシンボルがしばしば使われます。 | ||||||||||
start | あなたのインスタンス生成関数によって受け取られた引数 ED_START の a_w.w.long フィールドを渡します。 | ||||||||||
duration | あなたのインスタンス生成関数によって受け取られた引数 ED_DURATION の a_w.w.long フィールドを渡します。 | ||||||||||
flags | 下記の定数リストを参照 | ||||||||||
box | イベント表示情報を格納するために標準の Smallbox 構造体を使う場合、この引数は 0L になります。引数が 0 以外の場合、Smallbox は割り当てられません。代わりに、あなたが渡す、既にメモリ割り当て済みで、初期化された Box が使われます。あなたの free 関数が呼び出される時、あなたはこの Box を開放する役割を果します。0L を渡した場合、Smallbox によって使われたメモリは、あなたのために開放されます。 | ||||||||||
この関数は、Event のほとんどのフィールドを初期化します( box_new と同様、これは既存のイベントを渡され、イベント自体の生成は行ないません)。引数 flags のための定数は次の通りです。
あなたは、F_GROWY または F_GROWBOTH のいずれかを使わなければなりません。また、オブションとして、F_CONSTANTWIDTH を使います。 |
event_box |
||
Event の矩形領域をセットするために、event_box を使います。 | ||
void event_box (Event *evnt, short top, shortbottom); | ||
evnt | あなたのオブジェクトへのポインタ | |
top | あなたのインスタンス生成関数によって受け取られた引数 ED_TOP の a_w.w_long フィールドを渡します。 | |
bottom | あなたのインスタンス生成関数によって受け取られた引数 ED_BOTTOM の a_w.w_long フィールドを渡します。 | |
この関数は Event の矩形領域を初期化します。これについて簡単に説明します。この関数は event_new の後に呼び出さなければなりません。そうすることによって、Event の 開始時刻(start)と持続時間(duration)の値を使って、Event の矩形領域の現在位置を計算することができます。 |
次のものは、event_new および event_box の使い方を示す、インスタンス生成関数の標準的な実装の例です。ここでは、エディタのために Event を初期化しています。エディタはデータ型と同じ名前を持ち、Event は標準の Smallbox を使用しています。
void *myEditor_new(t_symbol *dataType, short argc, t_atom *argv) { MyEditor *x; x = (MyEditor *)newobject(myEditor_class);/* インスタンスの生成 */ /* イベントの初期化 */ event_new((Event)x, /* イベント */ (void *)argv[ED_TRACK].a_w.w_obj, /* トラック */ dataType, /* データ型 */ argv[ED_MESSAGE].a_w.w_sym, /* メッセージ */ dataType, /* エディタ名 */ argv[ED_MESSAGE].a_w.w_sym, /* ラベル */ argv[ED_START].a_w.w_long, /* 開始時刻 */ argv[ED_DURATION].a_w.w_long, /* 持続時間 */ (long)F_GROWY | F_CONSTANTWIDTH, /* フラグ */ 0L); /* box */ event_box((Event)x, (short)argv[ED_TOP].a_w.w_long, /* 上端 */ (short)argv[ED_BOTTOM].a_w.w_long); /* 下端 */ /* 他の初期化はここで行います */ return (x); }
この関数は、初期化の際に editor_register に供給され、ユーザがタイムライントラックで新しいイベントを作ったときに呼び出されます。
void *myEditor_menu (t_symbol *dataType, t_symbol *message, void *track, void *obj, long start, Point pt); |
||
dataType | あなたのエディタが編集を行うことを登録したデータ型。あなたのインスタンス生成関数にこれを渡します。 | |
message | このイベントのためのメッセージ。あなたのインスタンス生成関数にこれを渡します。 | |
track | イベントを保持する親トラック。あなたのインスタンス生成関数にこれを渡します。 | |
obj | このイベントによって送られるメッセージのレシーバ。ほとんどの場合、この引数 obj は無視できます。これは、レシーバが eventStart や eventEnd を受け取るとき、これがあなたのエディタにも渡されるためです。しかし、あなたのエディタがオブジェクトの持つデータを表示している場合、この参照を保持することは重要になります。QuickTime ムービーエディターはこの情報を保持していますが、それは、ムービーのサムネイルを表示するために Max のムービーオブジェクトに格納されたムービーにアクセスする必要があるからです。 | |
start | このイベントの開始時刻(ミリセカンド). | |
pt | このイベントの位置を決めるためにユーザがクリックした場所。この位置がイベントの矩形領域の左上隅になります。 | |
この関数は、初期化の際に editor_register に供給され、ユーザがタイムライントラックで新しいイベントを作ったときに呼び出されます。メニュー関数は生成ルーチンの結果を返さなければなりません。結果は、新しく生成されたオブジェクトへのポインタ、または、生成時にエラーが発生した場合は 0 になります。 一般的に、メニュー関数では、あなたのオブジェクトの生成ルーチンに渡すための Atom の配列を組み立てます。この配列は、ファイルが読み込まれる際に生成関数がタイムラインオブジェクトから直接受け取るものと同じ引数フォーマットを取ります。 次のものは、エディタのための一般的なメニュー関数の例です。これは、int データ型のためのメッセージを扱います。使用されている定数は timelineEvent.h で宣言されています。 void *myEditor_menu(t_symbol *dataType, t_symbol *message, void *track, void *obj, long start, Point pt) { MyEditor *x; long dur; t_atom a[18]; SETOBJ(a + ED_TRACK,(void*)track); /* イベントのトラック */ SETSYM(a + ED_MESSAGE,message); /* イベントのメッセージ */ SETLONG(a + ED_START,start); /* イベントの開始時刻 */ dur = track_pixToMS(track,132); SETLONG(a + ED_DURATION,dur); /* イベントの持続時間 */ SETLONG(a + ED_TOP,(long)pt.v); /* box の上端 */ SETLONG(a + ED_BOTTOM,(long)pt.v+64); /* box の下端 */ x = myEditor_new(dataType,6,a); return (x); } |
event_spool |
||
Event の再描画が行われるようにしたい場合、event_spool を使います。 | ||
void event_spool (Event *evnt); | ||
evnt | 描画されるイベント | |
イベントのアピアランスに影響を及ぼすような変更を行った場合、その後に event_spool を呼び出します。これは、あなたのインスタンス生成ルーチンで行う必要があります。標準のエディタメッセージのコンテキスト外の関数によって単にオブジェクトの状態を再描画したい場合、event_spool を呼び出すことができます。この場合には、上記のセクションで説明されたセットアップを行う必要がない点に注意して下さい。 |
エディタを動作させるためには、psave、eventStart、および update メッセージを実装しなければなりません。これらのメッセージの背後にあるコンセプトは、パッチャー用にユーザインターフェイスオブジェクトを書くためのものと、全く同様です。このセクションでは、個々のメッセージに加え、メッセージに応答するメソッドを書く際に役立つような、タイムラインルーチンについても記述しています。
psave |
||
psave メッセージは、あなたのエディタがイベントを保存する必要がある場合に送られます。 | ||
バインディング |
||
addmess (myobject_psave, "psave", A_CANT, 0); | ||
宣言 |
||
void myobject_psave(myObject *x, Binbuf *dest); | ||
dest | オブジェクトの保存のためにメッセージを書き出すBinbuf | |
このメッセージは、イベントがコピーされるか、タイムラインファイルが保存された時に、あなたのオブジェクトに対しその内容を保存するために送られます。最初の9個の引数はすべてのイベントに標準となるもので、必ず保存されなければなりません。そして、関数 event_save によって、Atom の配列の中に置かれます。その後に、あなたのエディタインスタンスをリストアするために必要な追加データを置くことができます。そして、この配列を、binbuf_insert に渡しますが、この第1引数には dest を用います。event_save 関数の記述の後にあるサンプルを参照して下さい。psave メソッドへの引数は、パッチャーのユーザインターフェイスオブジェクトのケースやノーマルオブジェクトの save メッセージのためのメソッドと同じである点に注意して下さい。 |
eventStart |
||
eventStart メッセージは、イベントの矩形領域の左端を時間が通過した時に送られます。 | ||
バインディング |
||
addmess (myobject_eventStart, "eventStart", A_CANT,0); | ||
宣言 |
||
void myobject_eventStart(myObject *x, t_object *receiver); | ||
receiver | あなたのエディタにリンクされている、アクションパッチの中のオブジェクト、もしくはアクションエクスターナル。あなたのエディタの内容は、これに対して送られなければなりません。サンプルは後述します。 | |
このメッセージは割り込みレベルで送られる可能性があります。従って、メソッドの動作に対しては通常の制限が適用されます。一つのデータ要素を持つエディタオブジェクトのために、通常、eventStart の実装には、引数 receiver へのメッセージの送信が含まれます。イベントの持続時間内にイベントのシリーズをスケジューリングするような、より複雑なオブジェクトの場合には、タイムラインに関連したスケジューリング機能が作られます。最初に、シンプルなケースでどのような実装が行なわれるかについて見てみましょう。次に示す例では、eventStart メソッドはレシーバに1つの整数(int) を送信します。これは、int データ型を扱うナンバーボックスエディタによって使われるものと同じ実装です。これは、レシーバに対し、メッセージセレクタ int を送信せずに、Event の e_message フィールドを使っている点に注意して下さい。 void myNumberEditor_eventStart(MyEditor *x, t_object *receiver) { t_atom val; val.a_w.w_type = A_LONG; val.a_w.w_long = x->m_value; typedmess(receiver,x->m_event.e_message,1,&val); } |
event_save |
||
Event を保存するための最初の9個の引数を準備する場合に、event_save を使います。 | ||
void event_save (Event *evnt, t_atom *buf); | ||
evnt | 保存されるイベント | |
buf | 9個以上のAtom からなる配列。event_save はイベントの保存に必要な標準的な情報をここに置きます。 | |
この関数は Event の標準的な9個の項目を、Atom の配列にコピーします。これによって、Event のデータ構造体を保存するための詳細について考えずに済みます。次のものは、event_save が使用する psave メソッドの実装例です。この後、データの追加部分を、binbuf_insert を呼び出す前に付け加えます。 void myEditor_psave(MyEditor *x, void *buf) { t_atom buffer[10]; event_save((Event)x,buffer); SETLONG(buffer+9,x->m_value); binbuf_insert(buf,0L,10,buffer); } |
まず、次のように、4つの整数値を持ち、Event の持続時間の中でそれらを送信するオブジェクトのためのエディタを書いていることを想像してみましょう。
イベントの矩形領域
Timeline オブジェクトはエディタの内部構造については全く知りません。そのため、私たちにとって必要な、後ろの3つのメッセージのための eventStart 関数を自動的に呼び出してはくれません。そして、Timelineオブジェクトによって用いられる「カレントタイム(現在時刻)」は Max のスケジューラとは全く異なるため、私たちはこれらのイベントのスケジュールのために Clock を生成することもできません。しかし、これは他の操作によって行うことができます。最もシンプルなケースであっても、ユーザが Event の途中で再生を停止した場合には、これらのメッセージが送信されないようにしたいでしょう。この状況を扱うために、Timeline オブジェクトは、タイムライン上での相対時間によって実行をスケジュールされた、タスクの内部リストを持っています。このリストに、関数 event_schedule を使ってタスクを載せます。
event_schedule |
||
Timelineでの相対時間だけ後に、メッセージをスケジュールする場合に、event_schedule を使います。 | ||
void event_schedule (Event *evnt, method fun, t_object *receiver, void *arg, long delay, long flags); | ||
evnt | この関数でスケジューリングするイベント | |
fun | タイムラインが指定された時刻に達した時に呼び出して欲しい関数。この関数の宣言の方法は下記を参照して下さい。 | |
receiver | あなたか送るメッセージのレシーバ | |
arg | あなたの関数に渡される、付加的な引数。 | |
delay | 関数が呼び出されるまでのタイムライン上でのディレイ。“ミリセカンド”で表されます。 | |
flags | オプション。次のような定数のうちの1つ。L_DIEONSTOP (1) 指定されたポイントに達する前の時点でタイムラインが停止された場合、関数は呼び出されません。 L_MUSTHAPPEN (2) 指定されたポイントに達する前の時点でタイムラインが停止された場合でも、関数は呼び出されます。通常、後者は、スケジューリングされるタスクがMIDI のノートオフメッセージのようなものである場合に使用されます。 | |
これは、event_schedule を使って、イベントの持続時間に基づいた、均一な間隔での4つのメッセージ送信を実装したものです。4つの整数 (integer) の値は、m_values という配列に格納されていると仮定しています。どのメッセージを送信しているかを知るために、配列 m_counter の中のカウンタが必要になります。 この実装には2つの関数が含まれています。1つは、メッセージ eventStart に応答して最初の値を送信するもの、もう1つは、他の3つのメッセージを送信するために、event_schedule によってスケジュールされたものです。 void myEditor_eventStart(myEditor *x, t_object *receiver) { t_atom at; at.a_w.w_long = x->m_values[0]; /* 最初の値を送信 */ at.a_type = A_LONG; typedmess(receiver,x->m_event.e_message,1,&at); x->m_delay = (long)((double)x->m_event.e_duration/3.0); /* イベント間のインターバル(時間間隔)を計算 */ x->m_counter = 1; /* 次に送信する値 */ event_schedule(x,myEditor_tick,receiver,0L, x->m_delay,(long)L_DIEONSTOP); } void myEditor_tick(myEditor *x, t_object *receiver) { t_atom at; at.a_w.w_long = x->m_values[x->m_counter++]; /* 次の値を送信 */ at.a_type = A_LONG; typedmess(receiver,x->m_event.e_message,1,&at); if (x->m_counter < 4) /* 再スケジュール */ event_schedule(x,myEditor_tick,receiver,0L, x->m_delay, (long)L_DIEONSTOP); } |
update |
||
update メッセージは、あなたのエディタがイベントを再描画しなければならない場合に送信されます。 | ||
バインディング |
||
addmess (myobject_update, "update", A_CANT, 0); | ||
宣言 |
||
void myobject_update (myObject *x, Rect *updateBox, Boolean preview); | ||
updateBox | 再描画するイベントの矩形領域の部分。これはイベントの矩形領域すべてではない場合もあります。 | |
preview | 一般に、常に0以外の値です。0の場合、あなたのオブジェクトをより速い方法で再描画しなければならないことを示します。 | |
このメッセージは、エディタが、タイムラインウィンドウ上に自分のデータを描画するために送信されます。タイムラインのコンテキストで送られる update メッセージと、パッチャーウィンドウで送られる update メッセージの違いは、アップデートされるウィンドウの領域を含む、更新する矩形領域 updateBox が渡されるという点です。あなたのイベント矩形領域の一部だけが updateBox と交差している場合、データの全てを再描画するのを避けることができ、それによってタイムラインウィンドウの描画をスピードアップします。あなたのオブジェクトが(QuickTime ムービーエディタのように)データをゆっくり描画する場合、updateBox に注意を払うことは特に重要です。あなたのオブジェクトのイベント矩形領域が、描画されるタイムラインウィンドウの完全に領域外にある場合、update メソッドは呼び出されません。 upadate メソッドが呼び出されると、イベントの 矩形領域(*e->e_rect、これは、Box の矩形領域ではなく、ポインタである点に注意して下さい。)は的確にオフセットされているため、その中に描画を行うことができます。しかし、矩形領域 や updateBox の外側に描画しないよう気をつけて下さい。矩形のフレームを描画する必要はありません。描画するのは内容だけです。矩形領域のサイズに基づいた内部の変数を計算する場合には、update メッセージ間で変更されるサイズのための準備をしておく必要があります。(例えば、ユーザによるズームイン、ズームアウトの場合)。通常、描画をする前に、update メソッドの中で矩形領域のサイズの変更を常にチェックしなければなりません。 |
info |
||
info メッセージは、あなたのイベントが選択され、ユーザが Max メニューから Get Info... を選んだ場合に送られます。 | ||
バインディング |
||
addmess (myobject_info, "info", A_CANT, 0); | ||
宣言 |
||
void myobject_info (myObject *x); | ||
info メッセージにメソッドをバインドすると、オブジェクトが選択されている場合、自動的に Max メニューの GetInfo... が有効になります。一般的には、エディタに格納されているデータやエディタの表示のためのパラメータの状態をユーザが変更できるようにするダイアログボックスを表示させます。ダイアログボックスがあなたのイベントのアピアランスを変更した場合、event_spool を呼び出して、Timeline オブジェクトに再描画を行うよう告げなければなりません。 |
event_avoidRect |
||
ダイアログボックスをあなたのイベントと関連して配置するために、event_avoidRect を使います。 | ||
void event_avoidRect (Event *evnt, short dialogID); | ||
evnt | あなたのイベント | |
dialogID | DLOG リソースのリソースID. event_avoidRect は、リソースのダイアログウィンドウ矩形領域を修正します。 | |
patcher_avoidRect と同様に、event_avoidRect は、ダイアログボックスの座標を変更して、可能な限り編集されるイベントの直下に配置します。エディタが info メソッドでダイアログを使う場合には、GetNewDialog を呼び出す前に、event_avoidRectを使います。 |
dblclick |
||
dblclick メッセージは、ユーザがあなたのイベント上でダブルクリックした場合に送信されます。 | ||
バインディング |
||
addmess (myobject_dblclick, "dblclick", A_CANT, 0); | ||
宣言 |
||
void myobject_dblclick (myObject *x, Point pt, short mods); | ||
pt | ダブルクリックの位置 | |
mods | このマウスクリックイベントのためにGetNextEvent によって返されるMacOS イベントレコードの modifiers フィールド。shift、option、command、capslock、control キーが押されていたかどうかを示します | |
ユーザがイベント矩形領域の上でダブルクリックを行った時に、このメッセージがあなたのエディタに送られます。あなたのオブジェクトが編集するデータを補助ウィンドウに表示する場合、このメッセージに応答してウィンドウを表示させることができます。 |
エディタは、イベントに含まれるデータをタイムラインウィンドウ内で直接編集したり、補助ウィンドウ内で編集したり、あるいは全く編集を行なわなかったりすることができます。エディタの最初のタイプの例はメッセージボックス、ナンバーボックス、そして funbuff オブジェクトのための efuncエディタ です。2番目のタイプの例は、etable、edetonate、そして、3番目ののタイプの例は、emovie です。エディタが click または key メッセージに応答する場合、タイムラインはそのイベントを「編集可能」なものとして扱い、イベントの矩形領域内でのマウスクリックを、トラック内での開始位置や垂直位置の変更を行なうためのドラッグとし使用するのではなく、エディタに渡すことができるようにします。。
メッセージ idle、click、key に与えられる名前は、オブジェクトが受け取ルメッセージと同じである点に注意して下さい。これらは、それ自身のウィンドウに示されます。したがって、あなたのオブジェクトが編集可能で、自分の補助ウィンドウを持っている場合、その補助ウィンドウは他のオブジェクトによって「所有される」ようにしなければなりません。しかし、Max のテキストエディタウィンドウはそれ自身の t_ed オブジェクトクラスのインスタンスに属しているため、これらについての考慮を気にすることなく、タイムラインエディタの補助ウィンドウとしてテキストエディタを使うことが可能です。
target イベントは、キーボード入力を受け取るイベントであるため、これは上で述べた規準によって「編集可能」でなければなりません。イベントがターゲットの場合、click メッセージを受け取ることができます。これはまた、定義されていれば、 cut、copy、paste のようなメニューコマンドを扱うことができ、同様に、それらを有効にするための chkmenu メッセージを扱うことができます。(Chapter 10 の chkmenu メッセージに関する記述を参照して下さい)。一般的に、ターゲットイベントは、ユーザが最後にクリックまたは選択したイベントになります。ターゲットイベントはタイムラインウィンドウの中で、常に、その周囲にマーキーを伴っています。
idle |
||
カーソルがイベント矩形領域の上にある場合、それを追跡するために idle メッセージが送られます。 | ||
バインディング |
||
addmess (myobject_idle, "idle", A_CANT, 0); | ||
宣言 |
||
void myobject_idle (myObject *x, Point pt, short *within); | ||
pt | ローカル座標で表される、カーソルの現在位置 | |
within | マウスがイベント矩形領域の「編集」部分の範囲内にあるときに、click メッセージの中で、この領域内でのマウスクリックをあなたのエディタに渡して欲しい場合、within を 1 にセットします。このidle メソッドの呼び出しでカーソルを変更した場合、そして、どのような場合においても click メッセージを受け取りたくない場合、within を -1 にセットします。カーソルをセットしておらず、タイムラインによってイベント矩形領域内でのマウスクリックを標準の方法によって取り扱って欲しい場合、within を 0 にセットします。標準の方法では、イベント矩形領域の境界線上、または1ピクセル内側でクリックが行なわれた場合に、エディタに対し click メッセージが送られます。この、イベント上でのクリックによって何が行なわれたかの決定は、エディタが click メッセージを受け取る直前に、idle メソッドによって行なわれます。 | |
idle メッセージは、カーソルがイベント内にある時に、それを追跡するために、あなたのオブジェクトに対して送られます。あなたは、pt によるカーソルの位置、または track_drawDragParamによるレジェンド(後述)の中の表示情報を変更することができます。idle メッセージを送る前に、Timeline オブジェクトは、、あなたのイベントのトラックの最上部ではなく、ウィンドウの最上部からの相対的な位置に、あなたのイベント矩形領域を調節します。 |
click |
||
ユーザがあなたの編集可能なイベントをクリックした時に、click メッセージが送信されます。 | ||
バインディング |
||
addmess (myobject_click, "click", A_CANT, 0); | ||
宣言 |
||
void myobject_click (myObject *x, Point pt, short dbl, short modifiers); | ||
pt | ローカル座標で表された、マウスクリックの位置。 | |
dbl | ダブルクリックならば0以外の値になります。あなたのオブジェクトが click メッセージに応答する場合、決してdblclick メッセージが送られないという点に注意して下さい。 | |
modifiers | このマウスクリックイベントのためにGetNextEvent によって返される イベントレコードの modifiers フィールド。shift、option、command、capslock、control キーが押されていたかどうかを示します | |
あなたのイベント矩形領域の中でユーザがクリック、またはダブルクリックした時に、エディタはこのメッセージを受け取ります。click メッセージに応答して何をおこなうべきかということについては、多くの戦略があります。あなたのオブジェクトがテキストエディタならば、例えば、TEClick を呼び出すかもしれません。エディタ efunc のように、オブジェクトの内容を描画によって編集できるものであれば、パッチャーにおけるインターフェイスオブジェクトが行うと同様に、wind_drag を使って drag 関数を提供するでしょう。 click メッセージを送信する前に、Timeline オブジェクトは、あなたのイベント矩形領域を、トラックとの上端とではなく、ウィンドウの上端との比較によって調整します。しかし、drag 関数にオブジェクト内での連続したマウスの動きを取り扱わせている場合、イベント矩形領域の上端と下端は是正されません。スクリーンを反映してこの座標をリロケート(再配置)するためには、後述の関数 event_offsetRect を使います。この関数を通じて、イベントの位置に関する対話的処理やタイムラインレジェンド(後述)での描画のための関数を呼び出すことの有用性がわかるでしょう。 |
key |
||
あなたのイベントがターゲットイベントである際に、ユーザがキーを押した場合、key メッセージが送られます。 | ||
バインディング |
||
addmess (myobject_key, "key", A_CANT, 0); | ||
宣言 |
||
void myobject_key (myObject *x, short key, short modifiers, short code); | ||
key | 押されたキーの ASCII コード | |
modifiers | 修飾キーの状態 | |
code | Macintosh のキーコード |
selected |
||
selected メッセージによって、タイムラインに対しあなたの選択状態を知らせることができます。 | ||
バインディング |
||
addmess (myobject_selected, "selected", A_CANT, 0); | ||
宣言 |
||
void myobject_selected (myObject *x, short *state); | ||
state | 編集を行うためにデータが完全に選択されている場合(例えば、全体のコピー、または複製はこれにあたるはずです)は、state を 1 に設定します。エディタが編集するテキストを持っていて、その一部分が選択されている場合(または、全く選択されていない場合)は、state を 0 に設定します。しかし、テキストが全て選択されている場合には、1 をセットします。 | |
エディタがこのメッセージを受け取った場合、あなたは上に掲げた基準に従って state をセットしなければなりません。データの一部分の選択が許可されない(efunc のようなもの)、または、常に論理的全体として編集する(ナンバーボックスのようなもの)エディタがあります。この場合には、常に state は 1 になります。 |
Qelem 関数の中や、wind_drag によって呼び出されるマウスドラッグトラッキングが行われている環境の下で、エディタの中での描画をセットアップするために、これらのルーチンが使われます。
track_setport |
||
カレントグラフポートを、タイムラインを含むウィンドウにセットする場合に、track_setport を使います。 | ||
GrafPort *track_setport (void *track) | ||
track | イベントの親トラック (evnt->e_track). | |
標準のエディタメッセージのコンテキスト外で描画を行う場合に、この関数が必要になります。 track_setport は、wind_setport や patcher_setport と同様のものです。トラックがあれば、確実に、トラックのグラフポートで描画が行われます。track_setport が 0 を返した場合には、その時点で、あなたのイベントを含むタイムラインウィンドウが見えない状態になっていることを意味しているため、何も描画すべきではありません。ユーザがパッチャーのコンテキスト内で Timeline オブジェクトを作り、そのウィンドウを閉じている場合、この状況は十分に起こり得ることです。また、あなたのインスタンス生成関数の中でも track_setport をチェックしなければなりません。この関数が0以外の値を返した場合、描画やグラフポート関連の呼出し(TextWidth のようなもの)は安全に行うことができます。そうでない場合、あなたのエディタがこのイベントから最初の update メッセージを受け取るまで、この呼出しを待たせておく必要があります。描画が完了した時、track_setport から返された非0の値を、Macintosh の Setport ルーチンに渡します。 |
event_offsetRect |
||
あなたのイベントの矩形領域を調整するために、event_offsetRect を使います。これは、領域内での描画の前に行われます。 | ||
short event_offsetRect (Event *evnt) | ||
evnt | イベント | |
wind_drag から呼び出されたマウストラック関数の中や queue 関数の中のような、標準でない環境での描画を行う前に、ウィンドウの上端から相対的となるようにイベントの矩形領域のオフセットを行う必要があります。 event_offsetRect はこれを行い、イベント長方形領域をリストアするための Macintosh ルーチン OffsetRect の垂直座標として使う値を返します。以下は、event_offsetRect の典型的な使い方です。 short offset; offset = event_offsetRect((Event *)x); /* ここで描画を行います。 */ OffsetRect(x->m_event.e_rect, 0,-offset); /* リストアのために打ち消しを行います */ |
track_clipBegin |
||
あなたのイベント諜報形領域が、トラックの境界線によって部分的に隠されている場合、現在のトラック長方形領域に描画を制限するために、track_clipBein を使います。 | ||
void track_clipBegin (void *track, Rect *clip); | ||
track | イベントの親トラック (evnt->e_track). | |
clip | カレントのトラック矩形領域が置かれている場所。これを利用して、clip と交差する部分だけを描画することによって、イベントの見えていない部分の描画を避けることができます。 | |
wind_drag から呼び出されたマウストラッキング関数が動作している間、あなたのイベント矩形領域を描画する前に、描画をカレントのトラック矩形領域内に制限することが必要です。その理由は、イベントが部分的にトラックの見えている部分の外側にあるかも知れないためです。これは、track_clipBegin と track_clipEnd を呼び出して、全ての描画で描画範囲を制限することによって行われます。eventStart メッセージによってセットされた queue 関数内のように、Timeline から上記のような直接のメッセージを受けることができない状況で描画を行う場合でも、track_clipBegin と track_clipEnd を使わなければなりません。既に述べた標準のメッセージセットの何れか(update、click 等)をあなたのエディタが受け取る場合には、すでにクリッピングは設定済みなため、これらの関数を用いる必要はありません。 |
track_clipEnd |
||
track_clipBegin によってセットされたクリッピング領域をリストア(復元)する場合に、track_clipEnd を使います。 | ||
void track_clipEnd (void *track); | ||
track | イベントの親トラック (evnt->e_track). | |
個々のtrack_clipBegin の呼出しに対して track_clipEnd の呼出しが対応していない場合、タイムラインウィンドウ内で、怪しい描画の動作が起きることが知られています。 |
次の例では、これらのセットアップルーチン全ての正しい順序を示しています。
void myEditor_draw (Event *e) { GrafPort *savePort; Rect clipRect; void *eventTrack; short offset; eventTrack = e->e_track; if (savePort = track_setport(eventTrack)) { offset = event_offsetRect(e); track_clipBegin(eventTrack,&clipRect); /* draw here */ track_clipEnd(eventTrack); OffsetRect(e->e_rect,0,-offset); SetPort(savePort); } }
これらのルーチンは、タイムラインウィンドウの x 座標と、イベントタイムの変換を可能にします。
track_pixToMS |
||
タイムラインウィンドウのピクセル間隔をミリセカンドで表される時間の値に変換するために、 track_pixToMS を使います。 | ||
long track_pixToMS (void *track, short pix); | ||
track | イベントの親トラック (evnt->e_track). | |
pix | ミリセカンドに変換したいピクセル値 | |
track_pixToMS は、トラックとピクセル間隔を与えると、タイムラインのその時点でののズームレベルに基づいて、このピクセル数に見合ったミリセカンドの値を返します。 |
track_MSToPix |
||
時間の値をタイムラインウィンドウのピクセル間隔に変換するために、track_MSToPix を使います。 | ||
short track_pixToMS (void *track, long time); | ||
track | イベントの親トラック (evnt->e_track). | |
time | ピクセル数に変換したい、ミリセカンドで表される時間の値 | |
track_MSToPix は、ミリセカンドで表される持続時間を与えると、タイムラインのその時点でのズームレベルに基づいて、この持続時間に見合ったピクセル数を返します。 |
track_posToMS |
||
タイムラインウィンドウ内の位置を、ミリセカンドで表されるタイムラインの開始位置からの時間に変換する場合に、track_posToMS を使います。 | ||
long track_pixToMS (void *track, short pix); | ||
track | イベントの親トラック (evnt->e_track). | |
pix | ミリセカンドに変換したいピクセル値 | |
track_posToMS は、トラックと位置を表す x 座標を与えると、タイムラインのその時点でのズームレベルに基づいて、その位置に見合ったミリセカンドで表されるイベントタイムを返します。 |
track_MSToPos |
||
時間の値を、タイムラインウィンドウ内の位置に変換する場合に、track_MSToPos を使います。 | ||
short track_MSToPos (void *track, long time); | ||
track | イベントの親トラック (evnt->e_track). | |
time | ピクセルに変換したい、ミリセカンドで表される時間の値 | |
track_MSToPos は、トラックとミリセカンドで表されるイベントタイムを与えると、タイムラインのその時点でのズームレベルに基づいて、その時間の値に見合ったタイムラインウィンドウ内の位置を表す座標を返します。 |
これらのルーチンは、click メッセージに応答して始められる wind_drag によるドラッグ関数の中で使われます。これらは、イベント矩形領域内で起こるユーザの編集動作が、現在の時間位置や、他の種類の情報によって導かれることを可能にします。イベントの idle メッセージの間に、パッチャーウィンドウのアシスタンスのような方法による案内を行うために、このルーチンを呼び出すこともできます。各々の関数はタイムラインウィンドウにアクセスするために使われるトラックの引数を取ります。
track_drawDragTime |
||
イベントに関連した2つの数値を描画するために、track_drawDragTime を使います。 | ||
void track_drawDragTime (void *track, long time1,long time2); | ||
track | イベントの親トラック (evnt->e_track). | |
time1 | 左側に描画したい数値 | |
time2 | 右側に描画したい数値 | |
この関数は、カレントの時間フォーマットに従った文字列に変換された2つの値を受け取り、タイムラインレジェンドに表示します。通常、これはイベント長方形領域がドラッグされる際に、始まりと終わりの時間を表示するために使われます。値 time1 は通常、移動させられるアイテムの左端の値とみなされ、time2 は右端の値と見なされます。1つの値だけを描画したい場合、track_drawTime を使うことができます。 |
track_drawDragParam |
||
イベントを記述している文字列を描画するために、track_drawDragParam を使います。 | ||
void track_drawDragParam (void *track, char *string); | ||
track | イベントの親トラック (evnt->e_track). | |
string | イベントについて表示する情報の C 文字列 | |
この関数は、イベント矩形領域内での編集動作に対応して変更されるパラメータのカレントの値を記述するために、タイムラインウィンドウのレジェンド(説明)部分に文字列を描画することを可能にします。efunc オブジェクトは、ドラッグされる点のカレントの X、Y の値を表示するためにこのルーチンを使います。 |
track_drawTime |
||
イベントに関連した1つの数値を描画するために、track_drawTime を使います。 | ||
void track_drawTime (void *track, long time); | ||
track | イベントの親トラック (evnt->e_track). | |
time | 描画する数値 | |
この関数は、タイムラインウィンドウのレジェンド部分に、1つの時間の値を描画します。 |
track_eraseDragTime |
||
タイムラインウィンドウのレジェンド(説明)を消去するために、track_eraseDragTime を使います。 | ||
void track_eraseDragTime (void *track); | ||
track | イベントの親トラック (evnt->e_track). | |
この関数は、上記の3つの関数によって描画されたものを消去します。 |