オブジェクト操作 by llmod PART5
llmodの拡張オブジェクトは、HSPでは非常に扱いにくいと思ったツリービューで締めます。
HSPではツリービューのことをツリーボックスと呼んでるようですが、ツリービューで統一します。
ツリービューとはファイル選択をするエクスプローラの左側と同じコントロールです。
ツリービューと言うのは、決められたエリア内にジャンルごとの見出しを書いておき、
ジャンルを指定すると、そのジャンルに紐付いた小見出し(下位の階層)を表示することができます。
階層の限界は調べてないのでわかりませんが、数十階層分メモリの許す限りいけるのではないでしょうか。
まだピンと来ない方のために説明をすると、当ページのサイトマップのような構造のことです。

ツリービューを使うには、Windowsのコモンコントロールを操作するため、
いつものようにllmod.asをインクルードします。
同時にツリービューを操作するためtreebox.asもインクルードする必要があります。
まず初めに行うことはtreebox命令でエリア及びスタイルの決定です。
書式は「treebox 幅, 高さ, スタイル」でカレントポジションに作成されます。
幅や高さは言うまでもなくツリービューのサイズのことですね。
スタイルはリストビュー同様、comctl32.dllのバージョンによって使用できないものがあります。
p3に1を指定すると、そのアイテムが子アイテムを持つ時、アイテム横に「+」か「−」が付きます。
どちらが付くかと言うと、そのアイテムの選択状態により決定されます。
そのアイテムが未開封の時は開けることを示す「+」が、開封済みの時は「−」が表示されます。
p3に2を指定すると、1つ下のアイテムが別ジャンルでなければ線で繋がれます。
別ジャンルでないというのは、同一の親アイテムである子アイテムならば、と言うことです。
p3に4を指定するとルートアイテム同士も線が引かれる様になります。
ただ、4と同時に2の指定も行っていなければ実際に線が引かれることはありません。
8を指定すると、アイテムテキストの編集を可能にしますが、使用しないでくださいとのことです。
指定し、テキストを編集する要領でクリック選択後に少し間を置いて再度選択をするとHSPが落ちます。
$10(10進数の16)の指定は、親ウィンドウにD&D操作の開始メッセージを送信しなくするものですが、
これも8同様、指定しないでくださいとのことです。
$20(同32)では、フォーカスがツリービュー以外でも選択状態にしたままに出来ます。
指定せずロストフォーカスすると再度選択するまでツリービューの選択アイテムがわからなくなりがちです。
$40(同64)では、アラビア語などの横書きだと右から左に読む時に使用されるもので、
日本語をメインに使っていると思われる皆さんのパソコンでは使用できません。
ここまでがWi95以降であれば同じ様に使用できるスタイルです。
以下に書くスタイルはバージョンが4.70以上であることや4.71以上である必要があるもの。
$80(同128)では、アイテムが入りきらない時にポップアップ表示されるツールチップがなくなります。
ツールチップ内容はアイテムテキストですが、表示したくなければ$80を指定しましょう。
$100(同160)では、各アイテム横にチェックボックスが付加されます。
ツリービューのチェック確認はリストビューと違い階層構造の為、容易ではありません。
時間が出来た時にでも載せておくかもしれませんが、とりあえず今は説明だけにしておきます。
$200(同512)では、アイテム上にカーソルがあると、そのアイテムテキストに下線が引かれます。
$400(同1024)では、選択アイテムのみが開かれます。(他の開いているアイテムは閉じます)
また、選択アイテムが子アイテムを持っているならシングルクリックのみで開きます。
$800(同2048)は、ツールチップをセットするために必要ですが、
他の処理と組み合わせる必要があるのでココでは解説しません。
$1000(同4096)は、クリックした箇所と同じY座標上に存在するアイテムを選択します。
子アイテムを持っているアイテムでも、アイテム上のクリックでなければ選択出来てもオープンできません。
また、スタイルに1か2、その両方を同時に指定すると、この$1000は使えません。
$2000(同8192)では、ツリービュー外にアイテムが来る様な形になってもスクロールバーが付加されず
スクロールもしなくなりますので完全に収まりきる場合にのみ使用しましょう。
$4000(同16384)を指定するとアイテムの高さに奇数値を設定できるようになります。
指定しない場合は偶数値を設定しなければならないとのこと。
$8000(同32768)では横にはみ出しても水平スクロールバーは付かなくなります。
以上のようなスタイルを組み合わせてツリービューを作成すると、statにハンドルが返ります。
#include "llmod.as"
#include "treebox.as"

	pos winx / 4, winy / 4 : treebox winx / 2, winy / 2, 7 // 1+2+4
	title "ツリービューハンドル:" + stat
	stop
複数のツリービューを配置する場合は、このハンドルで選択するツリーを選ぶこととなるので、 配置後にツリービューのIDを知っておく必要があるのなら必ず退避しておくようにしましょう。 treeboxだけではエリアを決めただけなのでココにツリーアイテムを入れなければ意味を成しません。 ツリービューにアイテムを追加するのはtreeadd命令で書式は下記の様になっています。 「treeadd 親アイテムID, テキスト, 追加方法, クローズ時アイコン,オープン時アイコン, アイテム値」 p1の親アイテムIDは、どのアイテムの子アイテムとするかを決めるわけですが、 新規にアイテムを追加する場合などは親となるアイテムが存在しませんね。 親のないアイテム、つまりルートアイテムは、p1に0を指定することで指定完了です。 アイテムを配置し、成功すると配置したアイテムIDがstatに返りますので、 そのアイテムを親とする場合は、そのIDを指定するようにしてください。 後で紹介しますが、アイテムIDは別の命令で取得できるので必ずしも退避しておく必要はありません。 p2のアイテムテキストはアイテム横に表示するタイトルテキストです。 p3の追加方法は、0又は省略で指定したアイテムを親として、その下位階層アイテムの最後に追加します。 1でアイテムの最初に追加し、2でもアイテムの最後に追加し、3でソートして追加します。 ソートして追加とは文字コードの昇順で何番目のアイテムとなるかが決定されます。 p4のクローズ時アイコンとは、閉じている「+」状態のアイコンを指定します。 指定の仕方は前章のリストビューの時と大体同じ。次の命令の所で説明します。 p5のオープンアイコンは開いている「−」状態のアイコンのことです。コレも後ほど…。 p6のアイテム値は配置するアイテムに持たせておく値のこと。 指定しなくても構いませんし、他と同じ値を指定しても構いません。 数値しか指定できませんが、どのようなルールでどんな値を持たせるかは各個人で自由に決めてください。 失敗すると0が入りますが、これらを指定し、うまく配置が出来るとstatにアイテムIDが返ります。 このIDもアイテム固有で、そのアイテムを指定するときに必要になるハンドルです。
#include "llmod.as"
#include "treebox.as"

	pos winx / 4, winy / 4 : treebox winx / 2, winy / 2, 7
	repeat 4, 1
		treeadd 0, "親" + cnt, 3, 0, 1
		parent = stat
		repeat 3, 1
			treeadd parent, "子" + cnt, 3, 0, 1
			child = stat
			repeat 5, 1
				treeadd child, "孫" + cnt, 3, 0, 1
			loop
		loop
	loop
	stop
アイコンはリストビューとほぼ同じと書きました。 その指定の仕方は、まずリストビューの時と同じでtreebox.asよりも先にimg.asをインクルードして、 get_iconでアイコンハンドルを取得し、treeiconでイメージリストを生成します。 treeiconの書式は「treeicon get_iconで取得したID, アイコン数」です。
#include "llmod.as"
#include "img.as"
#include "treebox.as"

	pos winx / 4, winy / 4 : treebox winx / 2, winy / 2, 7
	get_icon icon.0, "shell32.dll", 3, 1
	get_icon icon.1, "shell32.dll", 4, 1
	treeicon icon, 2 // イメージリストに追加
	repeat 4, 1
		treeadd 0, "親" + cnt, 3, 0, 1
		parent = stat
		repeat 3, 1
			treeadd parent, "子" + cnt, 3, 0, 1
			child = stat
			repeat 5, 1
				treeadd child, "孫" + cnt, 3, 0, 1
			loop
		loop
	loop
	stop
ツリービューのアイテムを命令を使って選択をする命令はtreeselです。 書式は「treesel アイテムID, 選択タイプ」になります。 p1のアイテムIDに選択したいツリービューアイテムのIDを指定します。 アイテムIDはtreeadd命令でアイテム追加直後に入るstatの値か、後に説明するtreegetで取得します。 p2のタイプに0または省略で、通常の選択(画面外ならスクロールして選択アイコンを展開モードにする)。 1で選択中アイコンのテキストを反転表示させます。 2にすると、選択アイテムを先頭に持ってきますが、環境によってはうまく行かないことがあるようです。 スクロール出来る環境であっても、エリアより下にまだアイテムがある場合にスクロールが可能となります。 p2に$10(10進数の16)を足して指定するとアイテムを展開する様になります。 $20(同32)ではアイテムを縮小します。 $30(同48)で逆の状態、つまり展開中だと縮小し、縮小中だと展開するようになります。
#include "llmod.as"
#include "img.as"
#include "treebox.as"

	dim id, 10
	objsize 50, 25
	pos winx / 4, winy / 5 : treebox winx / 2, winy / 4, 7
	pos winx / 4, winy / 2 : button "▲", change
	pos winx / 4 + 50, winy / 2 : button "▼", change
	get_icon icon.0, "shell32.dll", 3, 1
	get_icon icon.1, "shell32.dll", 4, 1
	treeicon icon, 2
	repeat 10
		treeadd 0, "Tree" + cnt, 0, 0, 1
		id.cnt = stat
	loop
	stop

*change
	if stat = 0 & (index > 0) : index--
	if stat = 1 & (index > 9) : index++
	treesel id.index // アクティブアイテムの展開
	treesel id.index, 1 // アクティブアイテムテキストを反転表示
	stop
ツリービューの、主にアイテムの情報を取得する命令はtreegetになります。 書式は「treeget 代入する変数, 取得タイプ, タイプにより決定, タイプにより決定」です。 p2が−1、p3が0、p4が0の時は、ツリービューのルートIDを取得します。 p2が0、p3が0、p4が0の時は、現在選択(反転表示している)しているアイテムのIDを返します。 p2が1、p3がアイテムID、p4が0の時は、p3で指定アイテムの次(下)にあるアイテムのIDを返します。 p2が2、p3がアイテムID、p4が0の時は、p3で指定アイテムの前(上)にあるアイテムのIDを返します。 p2が3、p3がアイテムID、p4が0の時は、p3で指定アイテムの親アイテムのIDを返します。 p2が4、p3がアイテムID、p4が0の時は、p3で指定アイテムと同じ親の最上のアイテムIDを返します。 p2が5、p3がアイテムID、p4が0の時は、ツリービューの最上に見えているアイテムのIDを返します。 なぜp3にアイテムIDの設定が必要なのでしょうね?必要ない気がしますが…? p2が6、p3がアイテムID、p4が1以上だと、アイテムの見出しテキストをp4バイト分だけ取得します。 p2が6、p3がアイテムID、p4が0又は省略だと、先頭から63バイト分確保します。 p1の変数サイズが64バイト未満であってもオーバーフローせずに確保はしてくれているようですが それはたまたまなのか、本当に安全かどうか責任を取れませんので大きめに確保するようにしましょう。 p2が6、p3がアイテムID、p4が−1の時は、p3で指定したアイテムのテキストをp1の内容に書き換えます。 またp2が6の時はstatにアイテム値(treeadd命令のp6)が返ります。 p2が7、p3がアイテムID、p4が0又は省略だと、アイテム値を取得します。 p2が7、p3がアイテムID、p4が1の時は、アイテムに持たせる値を再設定します。 他命令と異なり、複雑でわかりにくいパラメータ指定ですがツリービューの操作には欠かせない命令です。 使用頻度が高いと思いますので、頑張って使いまくり体で覚えちゃいましょう。
#include "llmod.as"
#include "img.as"
#include "treebox.as"

	sdim name, 16, 2
	pos winx / 4, winy / 5 : treebox winx / 2, winy / 4, 7
	pos winx / 4, winy / 2 : input name, winx / 4
	get_icon icon.0, "shell32.dll", 3, 1
	get_icon icon.1, "shell32.dll", 4, 1
	treeicon icon, 2
	repeat 10, 1
		treeadd 0, "Tree" + cnt, 0, 0, 1
	loop

*main
	treeget item // 選択中アイテムIDを取得
	treeget name.1, 6, item // 指定IDのテキストを取得
	if name ! name.1 {
		name = name.1
		objprm 0, name
	}
	wait 1
	goto *main
大概はツリービューを1つしか配置しないと思いますが、複数のビューを配置する場合もあるでしょう。 ツリービュー自体の選択をする命令はsel_treeboxです。 1つしか配置しない場合は必然的にそのツリービューが対象ですが、複数ある場合は アクティブにするツリービューを変更する当命令を使用しなければ最後に処理した方しか操作できません。 書式は「sel_treebox ツリービューID」です。 p1にツリービューを配置した時statに入った値を指定します。
#include "llmod.as"
#include "img.as"
#include "treebox.as"

	dim treeid, 2
	dim itemid, 2
	dim icon, 2
	sdim name, 12
	get_icon icon.0, "shell32.dll", 3, 1
	get_icon icon.1, "shell32.dll", 4, 1
	objsize 100, 25
	pos 100, 100 : treebox winx / 2 - 100, winy / 2, 7
	treeid = stat
	treeicon icon, 2
	pos winx / 2, 100 : treebox winx / 2 - 100, winy / 2
	treeid.1 = stat
	treeicon icon, 2
	pos 100, winy / 2 + 100 : combox index, , "左のビュー\n右のビュー"
	pos 200, winy / 2 + 100 : input name
	pos 300, winy / 2 + 100 : button "追加", add

*main
	objprm 1, "ItemId" + itemid.index
	index.1 = index
	repeat
		if index ! index.1 : break
		wait 1
	loop
	goto *main

*add
	sel_treebox treeid.index // アクティブにするツリービューを選択
	treeadd 0, name, , , 1
	itemid.index++
	goto *main
アイテムを追加すると自動的に次のアイテム名が決定されていました。 デフォルトセットアイテム名は「ItemId + アイテム数分」となっていましたね。 こういった場合に限ったわけではありませんが、アイテム数を取得するtreemax命令が用意されています。 コレを使えば前回のように退避させておく必要がなくなりますね。 書式は「treemax 取得したアイテム数をセットする変数, フラグ」です。 p2を1にすると、ツリービューに見えているアイテム数が、0の時には全アイテム数がp1にセットされます。 見えているアイテムとは、完全に(横ではなく縦のみ)見えているアイテムの数です。
#include "llmod.as"
#include "img.as"
#include "treebox.as"

	dim treeid, 2
	dim icon, 2
	sdim name, 12
	get_icon icon.0, "shell32.dll", 3, 1
	get_icon icon.1, "shell32.dll", 4, 1
	objsize 100, 25
	pos 100, 100 : treebox winx / 2 - 100, winy / 2, 7
	treeid = stat
	treeicon icon, 2
	pos winx / 2, 100 : treebox winx / 2 - 100, winy / 2, 7
	treeid.1 = stat
	treeicon icon, 2
	pos 100, winy / 2 + 100 : combox index, , "左のビュー\n右のビュー"
	pos 200, winy / 2 + 100 : input name
	pos 300, winy / 2 + 100 : button "追加", add

*main
	sel_treebox treeid.index
	treemax max // アイテム数を取得
	objprm 1, "ItemId" + max
	index.1 = index
	repeat
		if index ! index.1 : break
		wait 1
	loop
	goto *main

*add
	treeadd 0, name, , , 1
	goto *main
アイテムの削除をしたい時もあることでしょう。 削除を行うのはtreedel命令で、「treedel アイテムID」という書式になります。
#include "llmod.as"
#include "img.as"
#include "treebox.as"

	dim treeid, 2
	dim icon, 20
	objsize winx / 2, 50
	pos 0, winy - 50 / 2 : button "▼", move
	pos winx / 2, winy - 50 / 2 : button "▲", move
	repeat 2
		pos 0, winy + 50 * cnt / 2 : treebox winx, winy - 50 / 2, 7
		treeid.cnt = stat
		tmp = cnt
		repeat 20
			get_icon icon.cnt, "shell32.dll", cnt, 1
		loop
		treeicon icon, 20
		repeat 10
			tmp.1 = tmp * 10 + cnt
			treeadd 0, "サンプルのアイテム" + tmp.1, , tmp.1, tmp.1, tmp.1
		loop
	loop
	stop

*move
	id = stat
	sel_treebox treeid.id
	treeget itemid
	if itemid = 0 : stop
	treeget name, 6, itemid : id.1 = stat
	treedel itemid // 削除(移動用にツリービューから一旦取り除く)
	id = 1 - id
	sel_treebox treeid.id
	treeadd 0, name, , id.1, id.1, id.1
	stop
ツリービューのアイテム追加方法でソートして追加すると言うモードがありました。 その方式で追加する場合には、直接関係してこないので知らなくても問題ありませんが、 最初又は最後に追加していっていたが、やはり文字コード順でソートしたいとなった場合や、 ソートして追加していってもアイテムのテキストを途中で変更した時に再ソートしたい場合。 こういったときにこれから紹介するtreesort命令を使用しましょう。 書式は「treesort アイテムのID」で、指定したIDの子アイテムを文字コードで昇順に並び替えます。
#include "llmod.as"
#include "img.as"
#include "treebox.as"

	sdim name, 16
	gosub *treeset
	objsize winx / 3, 25
	pos winx / 4, winy / 2
	input name
	button "変更", setname
	button "並び替え", sort

*main
	gosub *getname
	repeat
		wait 1
		treeget index
		if index ! index.1 : break
	loop
	goto *main

*treeset
	pos winx / 4, winy / 8 : treebox winx / 2, winy / 3, 7
	repeat 15
		get_icon icon.cnt, "shell32.dll", cnt, 1
	loop
	treeicon icon, 15
	repeat 15
	treeadd 0, "SampleItemNo." + cnt, , cnt, cnt
	loop
	return

*getname
	treeget name, 6, index
	objprm 0, name
	index.1 = index
	treesel index, 1
	return

*setname
	treeget name, 6, index, -1
	goto *main

*sort
	treesort 0
	goto *main
いよいよツリービュー最後の命令に入ります。 今から説明するtreehit命令は、マウス下にあるアイテムのIDを取得するための物です。 どういったときに使うか…用途はいろいろあると思います。 例えば、エクスプローラのフォルダ階層のビューの場合、一度に全部取得すると あまりにも時間が掛かりすぎてしまうし、一度に全フォルダを開くことなんて滅多にないことでしょう。 それなのに初期化の度に全階層を取得するのは効率が悪すぎですよね。 では、どうすればよいか。 いきなり、最下層を開くこともありますが、通常は上から順番に開かれていきます。 キーボードで操作する場合は、現在選択されているアイテムが次に開かれるかもしれないものなので、 選択された段階で次のアイテムを取得しておけばよいでしょう。 問題は、マウスです。 マウスでの操作の場合、アイテムが選択しないまま中を開いてしまうことが可能です。 この命令がなければマウスで操作する時に次の階層情報を取得するタイミングがありません。 他にも、例えばマウス下にあるアイテムのヒントをどこかに表示する場合などにも使えますね。 さて、書式ですが「treehit」のみとなっています。 マウスカーソル下にアイテムがあればそのアイテムのIDが、なければ0がstatに入ります。
#include "llmod.as"
#include "img.as"
#include "treebox.as"

	sdim name, 16
	gosub *treeset

*main
	gosub *getname
	repeat
		wait 1
		treeget index
		treehit // マウス下のアイテムを取得
		if stat : index = stat
		if index ! index.1 : break
	loop
	goto *main

*treeset
	pos winx / 4, winy / 8 : treebox winx / 2, winy / 3, 7
	get_icon icon.0, "shell32.dll", 3, 1
	get_icon icon.1, "shell32.dll", 4, 1
	treeicon icon, 2
	randomize
	rnd r, 9
	repeat r + 1
		treeadd 0, "ParentNo." + cnt, , 0, 1, cnt
		parent.0 = stat
		parent.1 = cnt
		rnd r, 9
		repeat r + 1
			treeadd parent, "ChildNo." + cnt, , 0, 1, parent.1 * 10 + cnt
			child.0 = stat
			child.1 = cnt
			rnd r, 9
			repeat r + 1
				treeadd child, "GrandChildNo." + cnt, , 0, 1, parent.1 * 100 + (child.1 * 10) + cnt
			loop
		loop
	loop
	return

*getname
	color 255, 255, 255 : boxf : color
	if index = 0 : return
	index.1 = index
	treeget name, 6, index
	parent.0 = stat / 10 \ 10
	parent.1 = stat / 100
	pos winx / 4, winy / 2 : mes "アイテム情報"
	pos winx / 4, winy / 2 + 20 : mes "アイテム名 " + name
	pos winx / 4, winy / 2 + 40 : mes "アイテム値 " + stat
	pos winx / 4, winy / 2 + 60 : mes "親     " + parent.0
	pos winx / 4, winy / 2 + 80 : mes "親の親   " + parent.1
	return