#include "stdafx.h"
#include "RNSF7.h"
#include "kwd_search.h"
#include "kwd_treectrl.h"

BYTE   btOSMask = 0;				// 使用OSによるマスク情報
vector<ITEMGROUP> ig;
HTREEITEM ks_htRoot;
int	   num_of_items;		// 実際に使用可能なアイテムの数をカウント
vector<RKYFTIME> rft_last, rft_now;			// キーワードファイルのタイムスタンプを記録

// アイテムの文字列マッチを調べる
// 全部英小文字の中で比較を行うため、小文字へ変換してから渡すこと。
bool match_ig(HWND hDlg, const ITEMGROUP *ig, LPCSTR pszSearchStringLow)
{
	if (pszSearchStringLow == NULL || pszSearchStringLow[0] == 0) {
		MessageBox(hDlg, "内部エラー：パラメータがNULLまたは空文字です。", "match_ig", MB_OK | MB_ICONWARNING);
		return false;
	}

	// 機能項目を文字列サーチ
	if (sjisstr(ig->keyword.c_str(), pszSearchStringLow) != NULL) {
		return true;
	}

	return false;
}

// aとbのサイズが等しく、内容がすべて等しければtrue, そうでなければ false を返す。
bool rft_equals(vector<RKYFTIME> &a, vector<RKYFTIME> &b)
{
	vector<RKYFTIME>::size_type i, m;

	m = a.size();
	if (m != b.size()) {
		// サイズが等しくなければ偽
		return false;
	}

	for(i=0 ; i<m ; ++i) {
		// 1つでもひとしくない要素があれば偽
		if (CompareFileTime(&(a[i].ft), &(b[i].ft)) != 0 || a[i].rky.compare(b[i].rky) != 0) {
			return false;
		}
	}

	return true;
}

// CSVの文字列解析（カンマ区切りとなっていることを前提に、カンマで区切られた各文字列を取り出す）
// マルチスレッド非対応
// 使い方はstrtokとほぼ同じ
char *splitCsvItems(char *strline, char **context)
{
	static char *left; // 解析開始ポインタ

	if ((left == NULL && strline == NULL) || context == NULL) {
		// 引数エラー
		return NULL;
	}

	if (strline) {
		// 初回呼び出し時。各種変数を初期化
		left = strline;
		*context = strline;
	}
	else {
		// 継続呼び出し時
		left = *context;
	}

	if (**context == '\0' || **context == '\r' || **context == '\n') {
		// 初っぱなから終端だった場合、NULLを返却して終了していることを示す
		left = NULL;
	}
	else {
		// **contextが区切りか終端を示すまでループ
		for(; !(**context == ',' || **context == '\r' || **context == '\n' || **context == '\0'); ++(*context))
			;
	}
	// 区切りか終端を発見。
	if (**context == '\0') {
		// リアル終端ならばそのまま
	}
	else {
		// リアル終端以外（区切りか改行記号）ならば、そこに\0を上書き、leftから始まる要素文字列を生成
		**context = '\0';
		// 次の文字列の開始位置をポイント
		++(*context);
	}

	return left;
}

// キー／ハイブ置換用
const char *strLongHive[] = {"HKEY_LOCAL_MACHINE\\","HKEY_CURRENT_USER\\","HKEY_CLASSES_ROOT\\","HKEY_USERS\\","HKEY_CURRENT_CONFIG\\","HKEY_DYN_DATA\\","HKEY_PERFORMANCE_DATA\\","HKEY_PERFORMANCE_TEXT\\","HKEY_PERFORMANCE_NLSTEXT\\"};
const char *strShortHive[] = {"HKLM\\","HKCU\\","HKCR\\","HKU\\","HKCC\\","HKDD\\","HKPD\\","HKPT\\","HKPN\\"};

// 長いハイブ名のインデックスを返す。無ければ -1
int searchLongHive(const char *p_strLongHive)
{
	if (p_strLongHive) {
		for(int j=0 ; j<_countof(strLongHive) ; ++j) {
			if (strncmp(p_strLongHive, strLongHive[j], strlen(strLongHive[j])) == 0) {
				return j;
			}
		}
	}
	return -1;
}

int searchShortHive(const char *p_strShortHive)
{
	if (p_strShortHive) {
		for(int j=0 ; j<_countof(strLongHive) ; ++j) {
			if (strncmp(p_strShortHive, strShortHive[j], strlen(strShortHive[j])) == 0) {
				return j;
			}
		}
	}
	return -1;
}


// 検索用の情報を準備する
bool readySearch(HWND hDlg)
{
	// カレントフォルダ内のキーワードファイル(*.rky)を全検索
	WIN32_FIND_DATA520 wfd;
	HANDLE hFind = FindFirstFile520("*.rky", &wfd);
	if (hFind == INVALID_HANDLE_VALUE) {
		MessageBox(hDlg, "キーワードファイル(*.rky)が見つかりません。", "キーワードファイルの読込み", MB_OK | MB_ICONSTOP);
		rft_last.clear();
		return false;
	}
	else {
		RKYFTIME rft;
		do {
			rft.rky = wfd.cFileName;
			rft.ft = wfd.ftLastWriteTime;
			rft_now.push_back(rft);
		} while(0 != FindNextFile520(hFind, &wfd));
		FindClose(hFind);
	}

	// 前回とキーワード情報が同じならば、そこで終了
	if (rft_equals(rft_now, rft_last)) {
		rft_now.clear();
		return true;
	}
	else {
		rft_last.swap(rft_now);
		rft_now.clear();
	}

	// すでにOS情報判定済みなら、再度の判定は行わない
	if (btOSMask == 0) {
		// 現在使用中のWindowsのバージョンを調べ、ig->btWinOS用のマスクデータを作成する
		if (isNT) {
			if (winver < 500) {	// Windows NT 4.0
				btOSMask = WN4;
			}
			else if (winver == 500) { // Windows 2000
				btOSMask = W2K;
			}
			else if (winver < 600) { // Windows XP / Server 2003 / XP x64
				btOSMask = WXP;
			}
			else if (winver == 600) { // Windows Vista / Server 2008
				btOSMask = WVI;
			}
			else { // Windows 7 / Server 2008 R2 / それより上
				btOSMask = W7_;
			}
		}
#ifndef _WIN64
		else {
			if (winver <= 400) {	// Windows 95
				btOSMask = W95;
			}
			else if (winver < 490) { // Windows 98 / 98SE
				btOSMask = W98;
			}
			else { // Windows Me
				btOSMask = WME;
			}
		}
#endif /* _WIN64 */
	}

	// *.RKYよりキーワード情報を読み出す
	vector<RKYFTIME>::iterator irft;
	FILE *fp;
	UINT64 qwSize;
	char cEncodeFlag;
	vector<char> nsrc;

	ig.clear();
	ig.reserve(ITEMNUM);

	for(irft = rft_last.begin() ; irft != rft_last.end() ; ++irft) {
		
		qwSize = getFileSizeQ(irft->rky.c_str());
		cEncodeFlag = ENCODE_SJIS;
		nsrc.clear();
		
		if ( qwSize == -1 || qwSize >= 2u*1024u*1024u*1024u-16u ) {
			// エラー(-1==0xffffffffffffffff)の場合は取り扱わない
			// ファイルサイズが2GB以上の場合も取り扱わない
			MessageBox(hDlg, "キーワードファイルが見つからないか、大きすぎます。", irft->rky.c_str(), MB_OK | MB_ICONSTOP);
			return false;
		}

		if ( 0 != fopen_s(&fp, irft->rky.c_str(), "rbS") ) {
			// ファイルをオープンできない
			MessageBox(hDlg, "キーワードファイルを開けません。", irft->rky.c_str(), MB_OK | MB_ICONSTOP);
			return false;
		}

		// ファイルがwidecharか、multibyteかを調べる
		// (ネイティブで扱えるwidecharはUnicode[LE]のみ)
		// [BOMの識別]
		//   ff fe    … Unicode (Little Endian)
		//   fe ff    … Unicode (Big Endian)
		//   ef bb bf … UTF-8
		// 上記のいずれでもなければ、Shift-JISと見なして処理を行う
		{ int b1 = 0, b2 = 0, b3 = 0;
			b1 = fgetc(fp);
			b2 = fgetc(fp);
			if (b1 == 0xff) {
				if (b2 == 0xfe) {
					cEncodeFlag = ENCODE_UNICODE_LE;
				}
			}
			else if (b1 == 0xfe) {
				if (b2 == 0xff) {
					cEncodeFlag = ENCODE_UNICODE_BE;
				}
			}
			else if (b1 == 0xef) {
				if (b2 == 0xbb) {
					b3 = fgetc(fp);
					if (b3 == 0xbf) {
						cEncodeFlag = ENCODE_UTF8;
					}
				}
			}
			// これら以外でENCODE_SJIS(初期値)
		}

		// ファイルポインタを先頭に戻す
		fseek(fp, 0, SEEK_SET);

		// ファイルのサイズを計測
		long fsize = (long)qwSize;

		if ( fsize == 0 ) {
			// ファイルの中身がない
			MessageBox(hDlg, "キーワードファイルの中身がありません。", irft->rky.c_str(), MB_OK | MB_ICONSTOP);
			return false;
		}

		// vectorのメモリを確保
		// rscファイルを読み込み
		if (cEncodeFlag == ENCODE_UNICODE_LE || cEncodeFlag == ENCODE_UNICODE_BE) { // widecharのファイルだったら
			int mb_fsize;	// マルチバイト版ファイルサイズ

			fsize -= 2;								// -2はBOMの分
			vector<unsigned char> wsrc(fsize+2);	// +2は終端のNULL文字の分

			fseek(fp, 2, SEEK_SET);					// BOMを飛ばす
			if ( 1 > fread(&wsrc[0], fsize, 1, fp) ) {
				fclose(fp);
				MessageBox(hDlg, "キーワードファイル(UNICODE)の読み出しエラーです。", irft->rky.c_str(), MB_OK | MB_ICONSTOP);
				return false;
			}
			wsrc[fsize-2] = wsrc[fsize-1] = 0;
			if (cEncodeFlag == ENCODE_UNICODE_BE) {
				unsigned char ucTemp;
				// BE→LE へ上下バイト入れ替え
				for(long i=0 ; i<fsize-2 ; i+=2) {
					ucTemp = wsrc[i];
					wsrc[i] = wsrc[i+1];
					wsrc[i+1] = ucTemp;
				}
			}

			mb_fsize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR)&wsrc[0], -1, NULL, 0, NULL, NULL);
			nsrc.resize(mb_fsize+1);
			fsize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR)&wsrc[0], -1, &nsrc[0], mb_fsize, NULL, NULL);
			--fsize;
		}
		else if (cEncodeFlag == ENCODE_UTF8) { // UTF-8のファイルだったら
			// UTF8 → Unicode[LE] → Shift-JIS の順にエンコードを行う
			int mb_fsize;			// マルチバイト(SJIS)版ファイルサイズ
			int wc_fsize;			// ワイドキャラクタ(Unicode)版ファイルサイズ
			string  src_utf8;

			fsize -= 3;								// -3はBOMの分

			// UTF-8としてファイルを読み込む
			fseek(fp, 3, SEEK_SET);					// BOMを飛ばす
			nsrc.resize(fsize+1);					// +1は終端のNULL文字の分
			if ( 1 > fread(&nsrc[0], fsize, 1, fp) ) {
				fclose(fp);
				MessageBox(hDlg, "キーワードファイル(UTF-8)の読み出しエラーです。", irft->rky.c_str(), MB_OK | MB_ICONSTOP);
				return false;
			}
			nsrc[fsize] = 0;

			// UTF-8→Unicode[LE]変換
			wc_fsize = MultiByteToWideChar(CP_UTF8, 0, &nsrc[0], -1, NULL, 0);
			vector<wchar_t> wsrc(wc_fsize+1);
			MultiByteToWideChar(CP_UTF8, 0, &nsrc[0], -1, &wsrc[0], wc_fsize);

			// Unicode[LE]→SJIS変換
			mb_fsize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR)&wsrc[0], -1, NULL, 0, NULL, NULL);
			nsrc.resize(mb_fsize+1);
			fsize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR)&wsrc[0], -1, &nsrc[0], mb_fsize, NULL, NULL);
			--fsize;
		}
		else { // その他はすべてSJIS
			nsrc.resize(fsize+1);
			if ( 1 > fread(&nsrc[0], fsize, 1, fp) ) {
				fclose(fp);
				MessageBox(hDlg, "キーワードファイル(Shift-JIS)の読み出しエラーです。", irft->rky.c_str(), MB_OK | MB_ICONSTOP);
				return false;
			}
			nsrc[fsize] = 0;
		}
		fclose(fp);

		if ( fsize <= 0 ) {
			MessageBox(hDlg, "キーワードファイルの中身がありません。", irft->rky.c_str(), MB_OK | MB_ICONSTOP);
			return false;
		}

		char *line, *tmp;
		char *next = NULL, *next_comma = NULL;
		int  j = 0, ci;
		bool bOsError = false;
		ITEMGROUP ig0;
		string lineitem[10];		// カンマ区切りで分解した要素
		string restoredLine;		// ^Aによる省略部分を復元した文字列
		bool bRegFlag;				// "*R" でレジストリと見なし、その後のレジストリキーの展開を行うフラグ
		
		line = strtok_s(&nsrc[0], DELIM_R, &next);
		while(line != NULL && line[0] != 0) {
			++j;

			// 検索用キーワード
			tmp = splitCsvItems(line, &next_comma);
			for(ci=0 ; ci<10 ; ++ci) {
				if (tmp == NULL) {
					lineitem[ci].clear();
				}
				else if (tmp[0] != '\x01') {
					lineitem[ci] = tmp;
				}
				tmp = splitCsvItems(NULL, &next_comma);
			}

			// 検索用キーワード再構成（^Aによる省略部分を再構成したモノ）
			restoredLine = lineitem[0];
			for(ci=1 ; ci<10 ; ++ci) {
				restoredLine.append(",");
				restoredLine.append(lineitem[ci]);
			}
			line = _strdup(restoredLine.c_str());
			ig0.keyword = line;

			// R-Script名称の取得
			tmp = splitCsvItems(line, &next_comma);
			if (tmp == NULL) {
				char s[1025];
				wsprintf(s, "キーワードファイル %s の %d行目で R-Scriptファイル名 を読もうとして失敗しました。", irft->rky.c_str(), j);
				MessageBox(hDlg, s, "readySearch", MB_OK | MB_ICONWARNING);
				break; // エラー:ここで終了
			}
			ig0.rsc = tmp;

			// CtrlIDの取得
			tmp = splitCsvItems(NULL, &next_comma);
			if (tmp == NULL) {
				char s[1025];
				wsprintf(s, "キーワードファイル %s の %d行目で コントロールID を読もうとして失敗しました。", irft->rky.c_str(), j);
				MessageBox(hDlg, s, "readySearch", MB_OK | MB_ICONWARNING);
				break; // エラー:ここで終了
			}
			ig0.id = tmp;

			// OS情報の取得
			tmp = splitCsvItems(NULL, &next_comma);
			if (tmp == NULL) {
				char s[1025];
				wsprintf(s, "キーワードファイル %s の %d行目で OS制限情報 を読もうとして失敗しました。", irft->rky.c_str(), j);
				MessageBox(hDlg, s, "readySearch", MB_OK | MB_ICONWARNING);
				break; // エラー:ここで終了
			}
			ig0.bOS = 0;
			if (tmp[0]) {
				switch (tmp[0]) {
					case '5':
						ig0.bOS |= W95;
						ig0.keyword.append(";win95;windows95");
						break;
					case '-':
						break;
					default:
						bOsError = true;
						break;
				}

				if (tmp[1]) {
					switch (tmp[1]) {
						case '8':
							ig0.bOS |= W98;
							ig0.keyword.append(";win98;windows98");
							break;
						case '-':
							break;
						default:
							bOsError = true;
							break;
					}

					if (tmp[2]) {
						switch (tmp[2]) {
							case 'M':
							case 'm':
								ig0.bOS |= WME;
								ig0.keyword.append(";winme;windowsme");
								break;
							case '-':
								break;
							default:
								bOsError = true;
								break;
						}

						if (tmp[3]) {
							switch (tmp[3]) {
								case '4':
									ig0.bOS |= WN4;
									ig0.keyword.append(";winnt4.0;windowsnt4.0");
									break;
								case '-':
									break;
								default:
									bOsError = true;
									break;
							}

							if (tmp[4]) {
								switch (tmp[4]) {
									case '2':
										ig0.bOS |= W2K;
										ig0.keyword.append(";win2000;windows2000;win2k;windows2k");
										break;
									case '-':
										break;
									default:
										bOsError = true;
										break;
								}

								if (tmp[5]) {
									switch (tmp[5]) {
										case 'X':
										case 'x':
											ig0.bOS |= WXP;
											ig0.keyword.append(";winxp;windowsxp");
											break;
										case '-':
											break;
										default:
											bOsError = true;
											break;
									}

									if (tmp[6]) {
										switch (tmp[6]) {
											case 'V':
											case 'v':
												ig0.bOS |= WVI;
												ig0.keyword.append(";winvista;windowsvista;longhorn;windowsserver2008;winsrv2008");
												break;
											case '-':
												break;
											default:
												bOsError = true;
												break;
										}

										if (tmp[7]) {
											switch (tmp[7]) {
												case '7':
													ig0.bOS |= W7_;
													ig0.keyword.append(";win7;windows7;winseven;windowsseven");
													break;
												case '-':
													break;
												default:
													bOsError = true;
													break;
											}
										}
									}
								}
							}
						}
					}
				}
			}

			if (bOsError) {
				char s[1025];
				wsprintf(s, "キーワードファイル %s の %d行目の OS制限情報 に不正な文字があります('%s')。", irft->rky.c_str(), j, tmp);
				MessageBox(hDlg, s, "readySearch", MB_OK | MB_ICONWARNING);
				break; // エラー:ここで終了
			}

			// 機能項目名称の取得
			tmp = splitCsvItems(NULL, &next_comma);
			if (tmp == NULL) {
				char s[1025];
				wsprintf(s, "キーワードファイル %s の %d行目で 機能項目名称 を読もうとして失敗しました。", irft->rky.c_str(), j);
				MessageBox(hDlg, s, "readySearch", MB_OK | MB_ICONWARNING);
				break; // エラー:ここで終了
			}
			ig0.group = tmp;

			// 項目名の取得
			tmp = splitCsvItems(NULL, &next_comma);
			if (tmp == NULL) {
				char s[1025];
				wsprintf(s, "キーワードファイル %s の %d行目で 項目名 を読もうとして失敗しました。", irft->rky.c_str(), j);
				MessageBox(hDlg, s, "readySearch", MB_OK | MB_ICONWARNING);
				break; // エラー:ここで終了
			}
			ig0.item = tmp;

			// 見せ用キーワードは、この時点で確定。
			ig0.keyword_show = next_comma;
			// ここ以降は、キーワード解析でエラーが発生しても無視する。

			// 説明文（特に解析はしない）
			tmp = splitCsvItems(NULL, &next_comma);

			// レジストリ/INI種別
			// "*R" だったらレジストリとみなし、隠れワードに「レジストリ/registry」を追加
			bRegFlag = false;
			tmp = splitCsvItems(NULL, &next_comma);
			if (tmp != NULL) {
				if (lstrcmp(tmp, "*R") == 0) {
					ig0.keyword.append(";レジストリ/registry");
					bRegFlag = true;
				}
			} // エラーが発生しても無視

			// レジストリキー
			tmp = splitCsvItems(NULL, &next_comma);
			if (bRegFlag && tmp != NULL) {
				// HKEY_LOCAL_MACHINE ←HKLM
				// HKEY_CURRENT_USER  ←HKCU
				// HKEY_CLASSES_ROOT  ←HKCR
				// HKEY_USERS         ←HKU
				// HKEY_CURRENT_CONFIG←HKCC
				// 
				// 以下、Win9x用
				// HKEY_DYN_DATA←HKDD
				// HKEY_PERFORMANCE_DATA←HKPD
				// HKEY_PERFORMANCE_TEXT←HKPT
				// HKEY_PERFORMANCE_NLSTEXT←HKPN
				// 
				// これらを見つけたら、短縮形と、長い方の両方で検索できるように、プログラムでメモリ展開する。
				// "/" で区切られて複数存在している場合もあるので、 /+ 上記単語の場合も考慮できるものならする
				//
				ig0.keyword.push_back(';'); // 追加キーワードとして追加

				int i=0, j;
				while(tmp[i]) {
					// strLongHiveと同じであれば、strShortHive版を検索キーとして追加（小文字化が必要）
					j = searchLongHive(&tmp[i]);
					if (j >= 0) { // ヒットするstrLongHiveがあった
						// まずはstrShortHiveをコピー
						ig0.keyword.append(strShortHive[j]);

						// コピー元のポインタを、strLongHiveの後ろにポイント
						i += (int)strlen(strLongHive[j]);

						// '/'+ハイブ名 か 終端に到着するまで、単純に一文字ずつコピー
						for(; !(tmp[i] == 0 || (tmp[i] == '/' && (searchLongHive(&tmp[i]+1)>=0 || searchShortHive(&tmp[i]+1)>=0) )) ; ++i) {
							ig0.keyword.push_back(tmp[i]);
						}

						if (tmp[i] == '/') {
							// '/' だった場合は、次のHiveの一致を調べる
							ig0.keyword.push_back('/');
							++i;
							continue;
						}
						else {
							// 終端だったら終了
							break;
						}
					}

					// strShortHiveと同じであれば、strLongHive版を検索キーとして追加（小文字化が必要）
					j = searchShortHive(&tmp[i]);
					if (j >= 0) { // ヒットするstrLongHiveがあった
						// まずはstrShortHiveをコピー
						ig0.keyword.append(strLongHive[j]);

						// コピー元のポインタを、strLongHiveの後ろにポイント
						i += (int)strlen(strShortHive[j]);

						// '/'+ハイブ名 か 終端に到着するまで、単純に一文字ずつコピー
						for(; !(tmp[i] == 0 || (tmp[i] == '/' && (searchLongHive(&tmp[i]+1)>=0 || searchShortHive(&tmp[i]+1)>=0) )) ; ++i) {
							ig0.keyword.push_back(tmp[i]);
						}

						if (tmp[i] == '/') {
							// '/' だった場合は、次のHiveの一致を調べる
							ig0.keyword.push_back('/');
							++i;
							continue;
						}
						else {
							// 終端だったら終了
							break;
						}
					}

					++i;
				}
			} // エラーが発生しても無視

			// 検索用キーワードを小文字化
			tmp = _strdup(ig0.keyword.c_str());
			if (tmp) {
				ig0.keyword = sjislwr(tmp);
				safefree(tmp);
			}

			// キーワード検索リストへ追加
			ig.push_back(ig0);

			free(line);

			// 次の項目の検索
			line = strtok_s(NULL, DELIM_R, &next);
		}
	}

	return true;
}

// 検索用情報の破棄
void freeSearchData(void)
{
	ig.clear();
}

// 検索入力欄に入力された文字列を利用してサーチ処理を行う。
// 　引数：ダイアログハンドル
//   戻り値：true - 成功、false - 失敗（エラーダイアログは、必要であれば関数内で出力済み）
bool startSearch(HWND hDlg)
{
	// ツリービュー コントロールのハンドルを取得
	HWND hTreeView = GetDlgItem(hDlg, IDC_TREE1);

	// 利用不可のものを検出しない設定
	bool bOnlyEnable = false;
	if (SendDlgItemMessage(hDlg, IDC_CHECK_SHOW_ENABLE_ONLY, BM_GETCHECK, 0, NULL) == BST_CHECKED) {
		bOnlyEnable = true;
	}

	if (hTreeView == NULL) {
		MessageBox(hDlg, "内部エラー：ツリービューが見つかりません。", "startSearch", MB_OK | MB_ICONSTOP);
		return false;
	}

	// ツリービューのクリア
	TreeView_DeleteAllItems(hTreeView);

	// ツリービュー内部のメモリを暫定確保
	ListView_SetItemCount(hTreeView, ITEMNUM + RSCSTD);

	// ルート要素を追加
	HTREEITEM ks_htRoot = AddRootItemToTree(hTreeView, "いじくるつくーる");

	// 検索文字列の解析（DELIM区切りで文字列を抜き出し、文字列用の配列に格納する）
	int lenSearchText = GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT_SEARCH));

	num_of_items = 0; // 実際に使用可能なアイテムの数をカウント
	if (lenSearchText == 0) {
		// 文字列未入力の場合は、全追加。
		// 全アイテムをサーチ
		vector<ITEMGROUP>::iterator iter;
		for(iter=ig.begin() ; iter != ig.end() ; ++iter) {
			if (bOnlyEnable == false || (iter->bOS & btOSMask)) { // 現在のOSで使用可能か、使用可能以外表示しない設定でない
				AddItem(hTreeView, ks_htRoot, &iter[0]);
				++num_of_items;
			}
		}

		// ルート要素にカーソルを与え、ルート要素を展開する
		TreeView_SelectItem(hTreeView, ks_htRoot);
		TreeView_Expand(hTreeView, ks_htRoot, TVE_EXPAND);
	}
	else {
		vector<char> vSearchText;
		++lenSearchText;
		vSearchText.resize(lenSearchText);
		GetDlgItemText(hDlg, IDC_EDIT_SEARCH, &vSearchText[0], lenSearchText);

		vector<string> vSearchStrings;
		LPTSTR token, next_token = NULL;

		sjislwr(&vSearchText[0]);	// 検索文字列を小文字化しておく
		token = _tcstok_s(&vSearchText[0], DELIM_S, &next_token);
		while(token) {
			vSearchStrings.push_back(token);
			token = _tcstok_s(NULL, DELIM_S, &next_token);
		}

		vector<string>::iterator iter;
		vector<ITEMGROUP>:: iterator itig;
		// 全アイテムをサーチし、マッチするアイテムがあったら、ツリービューに追加していく
		for(itig=ig.begin() ; itig != ig.end() ; ++itig) {
			if (bOnlyEnable == false || (itig->bOS & btOSMask)) { // 現在のOSで使用可能か、使用可能以外表示しない設定でない
				for(iter = vSearchStrings.begin() ; iter != vSearchStrings.end() ; ++iter) {
					if (!match_ig(hDlg, &itig[0], iter->c_str())) { // 検索文字列不一致
						break;
					}
				}
				if (iter == vSearchStrings.end()) {
					// すべての検索文字列に一致した場合
					AddItem(hTreeView, ks_htRoot, &itig[0]);
					++num_of_items;
				}
			}
		}
		// ルート要素にカーソルを与え、ルート要素を展開する
		TreeView_SelectItem(hTreeView, ks_htRoot);
		TreeView_Expand(hTreeView, ks_htRoot, TVE_EXPAND);

		// 追加したアイテムの数が既定より少ない場合は、すべてのツリーを展開する
		if (num_of_items < MINIMUM_OF_ITEM) {
			ExpandItems(hTreeView);
		}

	}

	// ルートアイテムを表示
	TreeView_EnsureVisible(hTreeView, ks_htRoot);
	
	return true;
}
