トラバースキー詳細(続き)

更に、Control の translateTraversal を見てみる。
が、こいつは、派生クラスでオーバーライドされているようなので、
コンボ・コントロールから、辿って行く。

(Combo.java)
boolean translateTraversal (MSG msg) {
	/*
	* When the combo box is dropped down, allow return
	* to select an item in the list and escape to close
	* the combo box.
	*/
	System.out.println("Combo translateTraversal");
	
	switch ((int)/*64*/(msg.wParam)) {
		case OS.VK_RETURN:
		case OS.VK_ESCAPE:
			if ((style & SWT.DROP_DOWN) != 0) {
				if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) {
					return false;
				}
			}
	}
	return super.translateTraversal (msg);
}

コメントにもあるように、ドロップダウン状態であれば、確定なりキャンセルなりしないといけないので、
トラバース処理はキャンセルするようだ。(falseを返す => 通常の DispatchMessage で処理される)
そうでなければ、更に親クラスの translateTraversal を呼ぶ。
Combo の親は、Composite なので、順に見ていく。

(Composite.java)
boolean translateTraversal (MSG msg) {
	if ((state & CANVAS) != 0 ) {
		if ((style & SWT.EMBEDDED) != 0) return false;
		switch ((int)/*64*/msg.wParam) {
			case OS.VK_UP:
			case OS.VK_LEFT:
			case OS.VK_DOWN:
			case OS.VK_RIGHT: 
			case OS.VK_PRIOR:
			case OS.VK_NEXT:
				int uiState = (int)/*64*/OS.SendMessage (msg.hwnd, OS.WM_QUERYUISTATE, 0, 0);
				if ((uiState & OS.UISF_HIDEFOCUS) != 0) {
					OS.SendMessage (msg.hwnd, OS.WM_UPDATEUISTATE, OS.MAKEWPARAM (OS.UIS_CLEAR, OS.UISF_HIDEFOCUS), 0);
				}
				break;
		}
	}
	return super.translateTraversal (msg);
}

Composite では、フォーカスインジケータ(多分、点線の枠の事)の制御を必要に応じて行っているだけみたい。
translateTraversal をオーバーライドしているのは、この2つだけのようであり、
ようやく、Control の translateTraversal に辿りついた。(と言っても、2つだけだったのね)
長いので、前半は省略して、後半の大事そうなところだけ抜粋。

	Event event = new Event ();
	event.doit = doit;
	event.detail = detail;
	display.lastKey = lastKey;
	display.lastAscii = lastAscii;
	display.lastVirtual = lastVirtual;
	display.lastNull = display.lastDead = false;
	if (!setKeyState (event, SWT.Traverse, msg.wParam, msg.lParam)) return false;
	Shell shell = getShell ();
	Control control = this;
	do {
		if (control.traverse (event)) {
			OS.SendMessage (hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
			return true;
		}
		if (!event.doit && control.hooks (SWT.Traverse)) return false;
		if (control == shell) return false;
		control = control.parent;
	} while (all && control != null);
	return false;

コントロール自身から初めて、順に親を辿りながら、各コントロールの traverse を呼び出していく。
traverse は、基本的に Control にしか実装されていないようだ。
各コントロールが、このメソッドをオーバーライドする事は、今のところ無いようだ。

(Control.java)
boolean traverse (Event event) {
	/*
	* It is possible (but unlikely), that application
	* code could have disposed the widget in the traverse
	* event.  If this happens, return true to stop further
	* event processing.
	*/	
	sendEvent (SWT.Traverse, event);
	if (isDisposed ()) return true;
	if (!event.doit) return false;
	switch (event.detail) {
		case SWT.TRAVERSE_NONE:			return true;
		case SWT.TRAVERSE_ESCAPE:			return traverseEscape ();
		case SWT.TRAVERSE_RETURN:			return traverseReturn ();
		case SWT.TRAVERSE_TAB_NEXT:		return traverseGroup (true);
		case SWT.TRAVERSE_TAB_PREVIOUS:	return traverseGroup (false);
		case SWT.TRAVERSE_ARROW_NEXT:		return traverseItem (true);
		case SWT.TRAVERSE_ARROW_PREVIOUS:	return traverseItem (false);
		case SWT.TRAVERSE_MNEMONIC:		return traverseMnemonic (event.character);	
		case SWT.TRAVERSE_PAGE_NEXT:		return traversePage (true);
		case SWT.TRAVERSE_PAGE_PREVIOUS:	return traversePage (false);
	}
	return false;
}

処理の最初で、sendEvent (SWT.Traverse, event); を呼び出している。
これは、Control の親クラスである、Widget の メソッドである。
本体は、これだ。

void sendEvent (int eventType, Event event, boolean send) {
	if (eventTable == null && !display.filters (eventType)) {
		return;
	}
	if (event == null) event = new Event ();
	event.type = eventType;
	event.display = display;
	event.widget = this;
	if (event.time == 0) {
		event.time = display.getLastEventTime ();
	}
	if (send) {
		sendEvent (event);
	} else {
		display.postEvent (event);
	}
}

eventTable は、リスナーが登録されているオブジェクトである。
display.filters (eventType)は、親 Display が、指定イベントをフィルタしているかどうかをチェックしている。
要するに、イベントを送る先がなければ何もしない、あれば送るという事だ。
元に戻って、

	sendEvent (SWT.Traverse, event);
	if (isDisposed ()) return true;
	if (!event.doit) return false;

ふむ。
sendEvent の結果、自分自身が Dispose されるかもしれないという事か?
その場合は、イベント処理済みとなる。
sendEvent の先で、doit が false に設定された場合、ここで終了。

	switch (event.detail) {
		case SWT.TRAVERSE_NONE:			return true;
		case SWT.TRAVERSE_ESCAPE:			return traverseEscape ();
		case SWT.TRAVERSE_RETURN:			return traverseReturn ();
		case SWT.TRAVERSE_TAB_NEXT:		return traverseGroup (true);
		case SWT.TRAVERSE_TAB_PREVIOUS:	return traverseGroup (false);
		case SWT.TRAVERSE_ARROW_NEXT:		return traverseItem (true);
		case SWT.TRAVERSE_ARROW_PREVIOUS:	return traverseItem (false);
		case SWT.TRAVERSE_MNEMONIC:		return traverseMnemonic (event.character);	
		case SWT.TRAVERSE_PAGE_NEXT:		return traversePage (true);
		case SWT.TRAVERSE_PAGE_PREVIOUS:	return traversePage (false);
	}
	return false;

その先は、detail の内容により、処理を振り分け。
どうやら、この先で呼ばれる、各メソッドを、必要に応じてオーバーライドする方針のようだ。
ちなみに、traverse メソッドが複数存在して混乱するが、
public な traverse メソッドは、上位階層(アプリ層など)から traverse 処理を実行させる物だ。
メソッドのオーバーロードも、便利なんだけど、やり過ぎると混乱するよなぁ...
さて、Combo コントロールでは、traverseEscape と traverseReturn がオーバーライドされているようだが、
それ以外は、基本的に、Control クラスの実装のみのようである。


結局、translateTraversal は、

		if (control.traverse (event)) {
			OS.SendMessage (hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
			return true;
		}
		if (!event.doit && control.hooks (SWT.Traverse)) return false;
		if (control == shell) return false;
		control = control.parent;

各コントロールで、内部イベント送信→トラバーサル処理を行い、
その処理の中で無効化された、もしくは、トラバース処理がフック対象であれば、中断。
必要な限り、上記の処理を、親コントロールを対象に繰り返す。
という事になる。