コンポーネントカテゴリープロパティのリスナー設定

#カテゴリープロパティのリスナー
コンポーネントを作ってみると、カテゴリープロパティを使わなければいけないような場面も出てきます。カテゴリープロパティはレコードで複数項目を保持できる便利なプロパティですが、保持する値が多いので、作りも少し複雑になっています。
カテゴリープロパティを使っていると、プロパティの時のようにリスナーを登録してプロパティ値の編集時に何らかの処理を行いたくもなってきます。そこで設定するのがリスナーです。

#Listener
リスナーはデザイナーで動作しますので、xscファイルへ記述します。
しかしながら、プロパティのとき使った「PropertyListener」ではなく「Listener」です。

```
<Listener class="クラス名"/>
```
Listenerを登録するということは、コンポーネントのイベントをハンドルするということです。
ということは・・・
カテゴリープロパティの編集イベントのみ処理できるような<b>便利なクラスは無い</b>ということです。

ここで指定するクラスは、com.infoteria.asteria.flowbuilder2.event.BaseObjectListener をインプリしたクラスである必要があります。

これはかなり敷居が高めな気がしますが・・・大丈夫です！

BaseObjectListenerのAbstractな実装としてBaseObjectAdapterは用意されています。よって、ほとんどの場合はこのクラスを継承することになるでしょう。

改めてこのListenerで何がしたいのか考えてみると、コンポーネントの持っているカテゴリープロパティの編集イベントを拾いたい！ですね。カテゴリープロパティの編集イベントを受け取るには、カテゴリープロパティにリスナーを設定する必要があります。
手順は以下のようになります。

１．BaseObjectListener でコンポーネントの作成時の処理を追加
　　created()メソッドを作成します。

２．コンポーネントからカテゴリープロパティを取得
　　メソッド getCategoryProperty() で取得します。

３．カテゴリープロパティにリスナーを追加
　　CategoryPropertyのaddCategoryPropertyListener()メソッドを呼びます

４．カテゴリープロパティリスナーで「やりたい事」を実行

ソースは、以下な感じになります。

```
import com.infoteria.asteria.flowbuilder2.component.Component;
import com.infoteria.asteria.flowbuilder2.event.BaseObjectAdapter;
import com.infoteria.asteria.flowbuilder2.event.BaseObjectCloneEvent;
import com.infoteria.asteria.flowbuilder2.event.BaseObjectEvent;
import com.infoteria.asteria.flowbuilder2.event.CategoryPropertyListener;
import com.infoteria.asteria.flowbuilder2.property.category.CategoryProperty;

//BaseObjectAdapterクラスを継承したクラスを作ります
public class CategoryVariablesAdapter extends BaseObjectAdapter {

	//カテゴリープロパティのリスナーには、このクラス「QCategoryPropertyController」を使うことにして固定でもっちゃいます。
	private CategoryPropertyListener _listener = new QCategoryPropertyController();
	
	public CategoryVariablesAdapter() {
		//今回は何もしない
	}
	
	//作成するときにカテゴリープロパティへリスナーを追加します。
	public void created(BaseObjectEvent e) {
		CategoryProperty prop = getCategoryProperty(e);
		if (prop != null) {
			prop.addCategoryPropertyListener(_listener);
		}
	}

	//破棄されるときにremoveCategoryPropertyListenerメソッドでリスナーを外します。
	public void destroyed(BaseObjectEvent e) {
		CategoryProperty prop = getCategoryProperty(e);
		if (prop != null) {
			prop.removeCategoryPropertyListener(_listener);
		}
	}
	
	//クローン時もcreatedと同じ処理を行います。
	public void cloned(BaseObjectCloneEvent e) {
		created(e);
	}

	//イベントが発生したコンポーネントから「ExtendedCategory」カテゴリープロパティを取得します。
	private CategoryProperty getCategoryProperty(BaseObjectEvent e) {
		Component c = (Component)e.getBaseObject();
		return (CategoryProperty)c.getAdditionalProperty("ExtendedCategory");
	}
}
```
destroyedのときのリスナーのremoveCategoryPropertyListenerも忘れずに。


```
import com.infoteria.asteria.flowbuilder2.event.CategoryPropertyEvent;
import com.infoteria.asteria.flowbuilder2.event.CategoryPropertyListener;

//CategoryPropertyListener の実装
public class QCategoryPropertyController implements CategoryPropertyListener {

	public QCategoryPropertyController() {
		//今回は何もしない
	}

	public void propertyChanged(CategoryPropertyEvent e) {
		System.out.println("プロパティが変更されたよ " + e.getOldValue() + " -> " + e.getNewValue());
		//ここで「やりたい事」を実行
	}

	public void itemAdded(CategoryPropertyEvent e) {
		System.out.println("追加されました　" + e.getItem().getName());
	}

	public void itemRemoved(CategoryPropertyEvent e) {
		System.out.println("削除されました　" + e.getItem().getName());
	}
}
```
propertyChangedメソッドで「やりたい事」を記述するのですが、
ここで注意しなければならないのは、「やりたい事」がデザイナー上でUndoするべき処理なのか考えるということです。

#Undo
もし「やりたい事」がカテゴリープロパティのとあるプロパティ値の変更であった場合、Undoした方が良いです。
他にもUI上なんらかの変更がユーザーに見えるような変更ならばUndoするべきでしょう。
もちろん、あえてUndoしないという手もあります。

Undoの実装方法は、[プラグイン開発者ガイド / 7 ストリーム情報の編集](https://help.asteria.com/documentation/warp/ja/1906/flow/sdk/guide/PluginGuide.html#h7) の「7.1 実装例」や「7.4 プロパティリスナーでのUndoの扱い方」などを参照してください。

#まとめ
今回はカテゴリープロパティのリスナーを追加してみました。
ヘルプとして[プラグイン開発者ガイド](https://help.asteria.com/documentation/warp/ja/1906/flow/sdk/guide/PluginGuide.html#h1)を参照しましたが、コンポーネントを開発する方もあまり読んでいないのかな？と思いますので、この機会に読んでみてください。