Chapter 14:
タイムラインエディタの書き方

タイムラインウィンドウはパッチャーウィンドウと同様な構造体を持っています。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 構造体

あなたのエディタオブジェクトは 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_newevent_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_MESSAGEa_w.w_sym フィールドを渡します。
  editor エディタの名前, editor_register に渡したものと同じシンボル。
  label 任意のテキスト; 引数 editor に渡したものと同じシンボルがしばしば使われます。
  start あなたのインスタンス生成関数によって受け取られた引数 ED_STARTa_w.w.long フィールドを渡します。
  duration あなたのインスタンス生成関数によって受け取られた引数 ED_DURATIONa_w.w.long フィールドを渡します。
  flags 下記の定数リストを参照
  box イベント表示情報を格納するために標準の Smallbox 構造体を使う場合、この引数は 0L になります。引数が 0 以外の場合、Smallbox は割り当てられません。代わりに、あなたが渡す、既にメモリ割り当て済みで、初期化された Box が使われます。あなたの free 関数が呼び出される時、あなたはこの Box を開放する役割を果します。0L を渡した場合、Smallbox によって使われたメモリは、あなたのために開放されます。
     
 

この関数は、Event のほとんどのフィールドを初期化します( box_new と同様、これは既存のイベントを渡され、イベント自体の生成は行ないません)。引数 flags のための定数は次の通りです。

#define F_GROWY 2 ドラッグによって y軸方向に拡大できます。テキストベースのオブジェクトに適しています。
#define F_GROWBOTH 32 x 軸方向と y 軸方向に独立して拡大できます。
#define F_CONSTANTWIDTH 16 持続時間は表示のスケーリングに反応します。Event 構造体のフラグ、e_constantWidth をセットします。

あなたは、F_GROWY または F_GROWBOTH のいずれかを使わなければなりません。また、オブションとして、F_CONSTANTWIDTH を使います。


event_box

     
  Event の矩形領域をセットするために、event_box を使います。
   
  void event_box (Event *evnt, short top, shortbottom);
     
  evnt あなたのオブジェクトへのポインタ
  top あなたのインスタンス生成関数によって受け取られた引数 ED_TOPa_w.w_long フィールドを渡します。
  bottom あなたのインスタンス生成関数によって受け取られた引数 ED_BOTTOMa_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 は無視できます。これは、レシーバが eventStarteventEnd を受け取るとき、これがあなたのエディタにも渡されるためです。しかし、あなたのエディタがオブジェクトの持つデータを表示している場合、この参照を保持することは重要になります。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番目のタイプの例は、etableedetonate、そして、3番目ののタイプの例は、emovie です。エディタが click または key メッセージに応答する場合、タイムラインはそのイベントを「編集可能」なものとして扱い、イベントの矩形領域内でのマウスクリックを、トラック内での開始位置や垂直位置の変更を行なうためのドラッグとし使用するのではなく、エディタに渡すことができるようにします。。

メッセージ idleclickkey に与えられる名前は、オブジェクトが受け取ルメッセージと同じである点に注意して下さい。これらは、それ自身のウィンドウに示されます。したがって、あなたのオブジェクトが編集可能で、自分の補助ウィンドウを持っている場合、その補助ウィンドウは他のオブジェクトによって「所有される」ようにしなければなりません。しかし、Max のテキストエディタウィンドウはそれ自身の t_ed オブジェクトクラスのインスタンスに属しているため、これらについての考慮を気にすることなく、タイムラインエディタの補助ウィンドウとしてテキストエディタを使うことが可能です。

target イベントは、キーボード入力を受け取るイベントであるため、これは上で述べた規準によって「編集可能」でなければなりません。イベントがターゲットの場合、click メッセージを受け取ることができます。これはまた、定義されていれば、 cutcopypaste のようなメニューコマンドを扱うことができ、同様に、それらを有効にするための 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_setportpatcher_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_clipBegintrack_clipEnd を呼び出して、全ての描画で描画範囲を制限することによって行われます。eventStart メッセージによってセットされた queue 関数内のように、Timeline から上記のような直接のメッセージを受けることができない状況で描画を行う場合でも、track_clipBegintrack_clipEnd を使わなければなりません。既に述べた標準のメッセージセットの何れか(updateclick 等)をあなたのエディタが受け取る場合には、すでにクリッピングは設定済みなため、これらの関数を用いる必要はありません。


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つの関数によって描画されたものを消去します。