;============================================================ ; 2007/xx/xx ; S.Programs HHW / HHX Built-in Editor ; hs editor (HSP 3.0) ; http://sprocket.babyblue.jp/ ;============================================================ ;============================================================ #ifndef HHW_DEBUG ; 単体で run した場合 : HHW 単体モード #define HHW_DEBUG 1 #packopt name "hhw" #packopt hide 1 #packopt xsize 1 #packopt ysize 1 #include "hhx_lib.hsp" #include "hhx_db.hsp" #endif #include "gpm.hsp" ; Use GDI+ ;============================================================ ; マウス関係 #module #uselib "user32" #cfunc LoadCursorA "LoadCursorA" int, int #func SetCursor "SetCursor" int #func ScreenToClient "ScreenToClient" int, int #define global IDC_SIZEWE 32644 #define global IDC_SIZENS 32645 #define global IDC_SIZEALL 32646 #deffunc SetMouseCursor int p1 h = LoadCursorA(hinst, p1) if h : SetCursor h return #defcfunc rtmousex p = ginfo_mx, 0 ScreenToClient hwnd, varptr(p) return p.0 #defcfunc rtmousey p = 0, ginfo_my ScreenToClient hwnd, varptr(p) return p.1 #global ;============================================================ ; boxp module ; UI ボックス or エッジ 描画モジュール #module ; winapi #uselib "user32" #func DrawEdge "DrawEdge" int, var, int, int #uselib "uxtheme" #cfunc IsThemeActive "IsThemeActive" #cfunc OpenThemeData "OpenThemeData" int, wstr #func DrawThemeBackground "DrawThemeBackground" int, int, int, int, int, int #func DrawThemeEdge "DrawThemeEdge" int, int, int, int, sptr, int, int, sptr #func CloseThemeData "CloseThemeData" int ;-------------------------------------------------- ; boxpinit / stat = hTheme #deffunc boxpinit if varptr(OpenThemeData) { if hTheme { CloseThemeData hTheme hTheme = 0 } if IsThemeActive() { hTheme = OpenThemeData(hwnd, "tab") } } return hTheme ;-------------------------------------------------- ; boxp x0, y0, x1, y1, fEdge / stat = hTheme #deffunc boxp int p1, int p2, int p3, int p4, int p5 rect = p1, p2, p3, p4 if hTheme { if p5 { DrawThemeEdge hTheme, hdc, 0, 0, varptr(rect), p5, 0xf, 0 } else { DrawThemeBackground hTheme, hdc, 10, , varptr(rect) } } else { if p5 { DrawEdge hdc, rect, p5, 0xf } else { syscolor 15 boxf p1, p2, p3-1, p4-1 } } return hTheme #global ;============================================================ ; tricorn #module #deffunc tricorn int px, int py, int pdir ; top-x, top-y, direction (clock) if (pdir & 1) = 0 { dir = 1 - pdir ; (0, 2) -> (1, -1) repeat 4 boxf px, py-cnt*dir, px+cnt, py-cnt*dir boxf px-cnt, py-cnt*dir, px, py-cnt*dir loop } else { dir = 2 - pdir ; (1, 3) -> (1, -1) repeat 4 boxf px+cnt*dir, py-cnt, px+cnt*dir, py boxf px+cnt*dir, py, px+cnt*dir, py+cnt loop } return #global ;============================================================ ; hs エディタ モジュール ; (hhx_edit_start で起動 元のスクリプトに制御を返さず終了) #module ; winapi #uselib "user32" #func CharLower "CharLowerA" var #func MoveWindow "MoveWindow" sptr, sptr, sptr, sptr, sptr, sptr #func EnableWindow "EnableWindow" int, int #cfunc GetScrollPos "GetScrollPos" int, int #func SetWindowLongA "SetWindowLongA" sptr, sptr, sptr #cfunc MessageBox "MessageBoxA" int, sptr, sptr, int #func GetWindowRect "GetWindowRect" int, var #func MapWindowPoints "MapWindowPoints" int, int, var, int ; (menu api) #func CreateMenu "CreateMenu" #cfunc CreatePopupMenu "CreatePopupMenu" #func AppendMenu "AppendMenuA" int,int,int,sptr #func SetMenu "SetMenu" int,int #func DrawMenuBar "DrawMenuBar" int ; const #enum CMD_N = 333 #enum CMD_O #enum CMD_S #enum CMD_A #enum CMD_Q #enum CMD_V #enum CMD_E #enum CMD_SORT #enum CMD_OPTIM #enum CMD_INP #enum CMD_IH_STRONG #enum CMD_IH_TABLE #enum CMD_OPT_SHOWHV #enum CMD_OPT_HIDEHV #define ctype objhwnd(%1) objinfo(%1, 2) #define MAX_ID 2048 ;============================================================ ;============================================================ ; 起動・メインループ #deffunc hhx_edit_start str s1, str s2, str s3, str s4 appname = s1 appver = s2 appsig = s3 d_cmdline = s4 ; 設定ファイル名 CFGFILE = "hhw.cfg" ; 設定インデックス dim CFGDATA, 20 #const CFG_BORDERX 1 #const CFG_HRBORDERX 2 ; 設定セーブデータ デフォルト値 (1 1 1 ... 0 0 0 ...) repeat 10 CFGDATA(cnt) = 1 loop CFGDATA.CFG_BORDERX = 192 ; ピクセル CFGDATA.CFG_HRBORDERX = 50 ; パーセント ;============================================================ ; ウィンドウ作成 ;-------------------------------------------------- ; UI 変数初期化 str_insbox = "" ; 新規レコード追加 str_find = "" ; シンボル検索 str_tagbox = "" ; フィールドタグ追加 str_tagbox += "(フィールドタグ挿入 ...)\n" str_tagbox += "%prm ; パラメータリスト, パラメータ説明文\n" str_tagbox += "%inst ; 解説文\n" str_tagbox += "%sample ; サンプルスクリプト\n" str_tagbox += "%href ; 関連項目\n" str_tagbox += "%dll ; 使用プラグイン/モジュール\n" str_tagbox += "%ver ; バージョン\n" str_tagbox += "%date ; 日付\n" str_tagbox += "%author ; 著作者\n" str_tagbox += "%url ; 関連 URL\n" str_tagbox += "%note ; 備考 (補足情報等)\n" str_tagbox += "%type ; タイプ\n" str_tagbox += "%group ; グループ\n" str_tagbox += "%port ; 対応環境\n" str_tagbox += "%port+ ; 対応環境 (追加)\n" str_tagbox += "%port- ; 対応環境 (除外)\n" str_tagbox += "%portinfo ; 移植のヒント\n" pos_tagbox = 0 pos_reclist = 0 ; インデックス sdim str_edit, 65536 ; hs ソース sdim astr, 4096 ; テンポラリ ミニマム容量設定 sdim bstr, 4096 #ifdef FILEMAP_NAME_HHX ;---------------------------------------- ; symbol_select 設定 if d_cmdline ! "" { filemap_create FILEMAP_NAME_HHX ; 共有メモリを使用 ; dupptr map_hwnd, filemap_viewptr() , 4 , 4 ; 最初に起動した HHX の hwnd dupptr map_str, filemap_viewptr()+4, 1024, 2 ; 共有文字列 symbol_select = map_str filemap_close } #endif ;-------------------------------------------------- ; create window screen 0, ginfo_dispx+4, ginfo_dispy, 2 chdir dir_exe Window_resizable status_create ; ステータスバー 初期化 title appname ; メニュー 初期化 ; ファイル(F) hmenuFile = CreatePopupMenu() AppendMenu hmenuFile, 0, CMD_N, "新規作成(&N)" AppendMenu hmenuFile, 0, CMD_O, "開く(&O)" AppendMenu hmenuFile, 0, CMD_S, "上書き保存(&S)" AppendMenu hmenuFile, 0, CMD_A, "名前をつけて保存(&A)" AppendMenu hmenuFile, $800, 0, "" AppendMenu hmenuFile, 0, CMD_Q, "終了(&Q)" ; 表示(V) hmenuView = CreatePopupMenu() AppendMenu hmenuView, 0, CMD_OPT_SHOWHV, "%href ビューを表示(&S)" AppendMenu hmenuView, 0, CMD_OPT_HIDEHV, "%href ビューを非表示(&H)" ; ツール(T) hmenuTool = CreatePopupMenu() AppendMenu hmenuTool, 0, CMD_OPTIM, "レコードの並び順を最適化" AppendMenu hmenuTool, 0, CMD_SORT, "レコードの並び順をソート" AppendMenu hmenuTool, $800, 0, "" AppendMenu hmenuTool, 0, CMD_IH_TABLE, "インライン HTML 挿入" AppendMenu hmenuTool, 0, CMD_IH_STRONG, "インライン HTML 挿入" AppendMenu hmenuTool, $800, 0, "" AppendMenu hmenuTool, 0, CMD_INP, "HSP スクリプトからシンボルをインポート" ; ヘルプ(H) hmenuHelp = CreatePopupMenu() AppendMenu hmenuHelp, 0, CMD_V, "バージョン情報(&V)" ; メニューバー作成 CreateMenu : hmenu = stat AppendMenu hmenu, $10, hmenuFile, "ファイル(&F)" AppendMenu hmenu, $00, CMD_E, "編集...(&E)" AppendMenu hmenu, $10, hmenuView, "表示(&V)" AppendMenu hmenu, $10, hmenuTool, "ツール(&T)" AppendMenu hmenu, $10, hmenuHelp, "ヘルプ(&H)" SetMenu hwnd, hmenu DrawMenuBar hwnd ; UI ウィンドウ作成 ; レコード挿入 box objmode 2 font "MS ゴシック", 12 input str_insbox, , , 50 id_insbox = stat button gosub "レコード挿入", *ins_rec id_insbtn = stat ; シンボル検索 box input str_find, , , 20 id_find = stat ; レコードリスト listbox pos_reclist, , astr id_reclist = stat ; objhwnd(id_reclist) -> GWL_EXSTYLE -> 0 SetWindowLongA objhwnd(id_reclist), -20, 0 ; エディタツール combox pos_tagbox, 200, str_tagbox id_tagbox = stat button gosub "レコード削除", *del_rec id_delbtn = stat ; エディタ font "Terminal", 18 mesbox str_edit, , , 1, -1 id_edit = stat ; objhwnd(id_edit) -> GWL_EXSTYLE -> 0 SetWindowLongA objhwnd(id_edit), -20, 0 ; EM_LINEFROMCHAR で与えるべき値のタイプを判別 ; (Windows XP は文字数、それ以下ではバイト数) objprm id_edit, "、、\n" ; EM_LINEFROMCHAR sendmsg objhwnd(id_edit), 0xc9, 4, 0 TYPE_EM_LINEFROMCHAR = stat ; 0->bytes, 1->chars ; 割り込み oncmd gosub *event_WM_ACTIVATE, 0x0006 ; -- これをトップに書くこと oncmd gosub *event_WM_MOUSEWHELL, 0x020a oncmd gosub *event_WM_SIZE, 0x0005 oncmd gosub *event_WM_COMMAND, 0x0111 oncmd gosub *event_WM_MOUSEMOVE, 0x0200 onclick gosub *event_onclick onkey gosub *event_onkey onexit gosub *endapp timer_setlabel *event_WM_TIMER ;-------------------------------------------------- ; show window Window_load CFGFILE, CFGDATA objsel id_edit await ;-------------------------------------------------- ; ロード cur_file = d_cmdline ; ファイル名 gosub *loadhs ;-------------------------------------------------- ; ポーリング ループ timer_start repeat if cnt : wait 4 ; 割り込み先へはここから飛んで、ここへ帰ってくる ; タイトル設定 if cur_file = "" { astr = "無題" } else { astr = getpath(cur_file, 8) } if edited { astr += " *" } if id > 0 : astr += " [" + symbuf.id + "]" astr += " - " + appname if astr ! titlebk { titlebk = astr title titlebk } ; ID 切り替え -> 新しいパートバッファ ロード if pos_reclist ! id { id = pos_reclist if id >= 0 { str_edit = recbuf.id objprm id_edit, str_edit } gosub *redrawer } ; EDIT 変更された -> パートバッファ更新 & 再描画 if str_edit ! recbuf.id { recbuf.id = str_edit ; パートバッファに保存 edited = 1 ; 編集されたフラグ bksymbuf = symbuf.id ; シンボル バックアップ gosub *renewref ; パート hs 再解析 ; シンボルが変更された → リンク情報 再構築 if bksymbuf ! symbuf.id { bkid = id ; 現在 ID バックアップ gosub *rebuild_buf gosub *rebuild_recs if bkid >= ids : bkid = ids - 1 ; patch pos_reclist = bkid ; バックアップ ID 復元 id = bkid ; バックアップ ID 復元 ; LB 状態 復元 ; LB_SETTOPINDEX sendmsg objhwnd(id_reclist), 0x197, idxsbk, 0 ; 更新の結果、パートバッファ内容が変化した -> エディットボックス内容更新 if str_edit ! recbuf.id { ; EM_GETSEL a = 0 : b = 0 sendmsg objhwnd(id_edit), 0xb0, varptr(a), varptr(b) ; 編集文字列 str_edit = recbuf.id objprm id_edit, str_edit ; EM_SETSEL sendmsg objhwnd(id_edit), 0xb1, a, b ; EM_SCROLLCARET sendmsg objhwnd(id_edit), 0xb7, 0, 0 } } objprm id_reclist, id gosub *redrawer } ; シンボル名で選択 if symbol_select ! "" { repeat ids if symbuf.cnt = symbol_select { pos_reclist = cnt objprm id_reclist, pos_reclist break } loop symbol_select = "" } ; ---------- low priority ---------- ; 検索文字列が変わった -> 再描画 if str_find ! findstrbk { findstrbk = str_find ; 検索結果が 1 つしかなければ or 完全一致があれば直接ジャンプ icnt = 0 repeat ids-1, 1 if instr(symbuf.cnt, 0, str_find) >= 0 { i = cnt icnt++ } if symbuf.cnt = str_find { i = cnt icnt = 1 break } loop if (icnt = 1) & (i ! id) { symbol_select = symbuf.i continue } gosub *redrawer } ; 項目数が増えた -> ステータスバー 更新 if ids ! idsbk { idsbk = ids status_mes 2, ""+(ids-1)+" record(s)" } ; フィールドタグ 挿入 if pos_tagbox { notesel str_tagbox noteget astr, pos_tagbox getstr astr, astr, , ' ' i = instr(str_edit, 0, astr) if i >= 0 { a = i b = i + strlen(astr) ; EM_SETSEL sendmsg objhwnd(id_edit), 0xb1, a, b ; EM_SCROLLCARET sendmsg objhwnd(id_edit), 0xb7, 0, 0 } else { astr = "\n" + astr + "\n" a = strlen(str_edit) ; EM_SETSEL sendmsg objhwnd(id_edit), 0xb1, a, a ; EM_REPLACESEL sendmsg objhwnd(id_edit), 0xc2, 1, varptr(astr) } pos_tagbox = 0 objprm id_tagbox, pos_tagbox objsel id_edit } gosub *event_WM_TIMER loop ;-------------------------------------------------- *event_WM_TIMER ; ループで完全に取れないところ ; idx | hs がスクロール -> 再描画 ; (id == -1 のときは描画禁止タイミング) a = GetScrollPos( objhwnd(id_reclist), 1) b = GetScrollPos( objhwnd(id_edit), 1) if ((a ! idxsbk) | (b ! hssbk)) & (id >= 0) { idxsbk = a hssbk = b gosub *redrawer } return ;============================================================ ;============================================================ ; subroutines ;============================================================ ; ファイル名設定 / セーブとロード ;-------------------------------------------------- ; ロードファイル名設定 (取得OK -> 0, エラー -> 1) #defcfunc loadfn dialog "hs", 16, "hs ファイル" if stat = 0 : return 1 cur_file = refstr return 0 ;-------------------------------------------------- ; セーブファイル名設定 (取得OK -> 0, エラー -> 1) #defcfunc savefn dialog "hs", 17, "hs ファイル" if stat = 0 : return 1 cur_file = refstr ; 自動拡張子 astr = cur_file CharLower astr if strmid(astr, strlen(astr) - 3, 3) ! ".hs" : cur_file += ".hs" return 0 ;-------------------------------------------------- ; ファイル名 リセット *nullfn cur_file = "" return ;-------------------------------------------------- ; 保存 *savehs gosub *rebuild_buf bsave cur_file, buf, strlen(buf) edited = 0 ; 編集フラグ off #ifdef FILEMAP_NAME_HHX ;---------------------------------------- ; セーブのタイミングで HHX へリロード指令 filemap_create FILEMAP_NAME_HHX ; 共有メモリを使用 dupptr map_hwnd, filemap_viewptr() , 4 , 4 ; 最初に起動した HHX の hwnd ; dupptr map_str, filemap_viewptr()+4, 1024, 2 ; 共有文字列 ; すでに filemap があれば : リロード メッセージ送信 if filemap_already_exists() { ; sendmsg map_hwnd, WM_APP_RELOAD ; SendMessage でなく PostMessage を使用 #uselib "user32.dll" #func PostMessage "PostMessageA" int,int,int,int PostMessage map_hwnd, WM_APP_RELOAD, 0, 0 } filemap_close #endif return ;-------------------------------------------------- ; ロード (パートバッファ recbuf 作成) *loadhs ; 作業画面 フラッシュ str_edit = "" objprm id_edit, str_edit gosub *redrawer title appname + " - Loading ..." titlebk = "" ; 次回タイトル再描画させる if cur_file ! "" { ; ファイルを開く exist cur_file if strsize >= 0 { sdim buf, strsize + 16 bload cur_file, buf } else { dialog "ファイル " + cur_file + " が見つかりません。", 1, appname cur_file = "" } } if cur_file = "" { ; 新規作成 buf = ";--------------------------------------------------\n" buf += "; HSP ヘルプ ソースファイル (hs 形式)\n" buf += "; hs ファイルの書式については、HS_BIBLE.txt を参照してください。\n" buf += "\n" buf += ";--------------------------------------------------\n" buf += "; この hs ファイルのデフォルトのフィールド値を設定\n\n" buf += "%dll\n; プラグイン/モジュール名 を記入\n\n" buf += "%ver\n; バージョン を記入\n\n" buf += "%date\n; 日付 を記入\n\n" buf += "%author\n; 著作者 を記入\n\n" buf += "%url\n; 関連 URL を記入\n\n" buf += "%note\n; 備考 (補足情報等) を記入\n\n" buf += "%type\n; タイプ を記入\n\n" buf += "%port\n; 対応環境 を記入\n\n" buf += "%portinfo\n; 移植のヒント を記入\n\n" } gosub *rebuild_recs edited = 0 ; 編集フラグ off return ;============================================================ ; データ処理 ;-------------------------------------------------- ; 全体バッファ buf 作成 *rebuild_buf sdim buf, 1048576 repeat ids if strlen(recbuf.cnt) = 0 : continue ; 末尾改行を強制 if peek(recbuf.cnt, strlen(recbuf.cnt)-1) ! 10 { recbuf.cnt += "\n" } ; 連結 buf += recbuf.cnt loop return ;-------------------------------------------------- ; パートバッファ recbuf 作成 (ファイル読み込み・レコードの追加・削除時に発生) ; パート情報構築 ; UI リスト リビルド *rebuild_recs ; 終端 buf += "\n%index" ; パートバッファ作成 // 出力 : [int ids], [str array recbuf] sdim recbuf, 64, MAX_ID repeat MAX_ID recbuf.cnt="#DEADBEEF#" ; 見えてはいけない部分を設定 loop ids = 0 lp = 0 p = 0 c_curr = C_VOID repeat ; 処理ライン取得 getstr astr, buf, p if strsize = 0 : break bp = p p += strsize a = peek(astr) if a = ';' : continue ; '%' エスケープ (%%hgoehgoe..) if wpeek(astr) = 0x2525 { memcpy astr, astr, strsize + 1, , 1 a = '*' } if a = '%' { ; コメント除去, 小文字化 getstr astr, astr, , ' ' getstr astr, astr, , ';' getstr astr, astr, , 9 CharLower astr ; レコード開始 if astr = "%index" { if ids = MAX_ID { dialog "** 警告 **\n\nレコード数が上限値 " + MAX_ID + " を超えました。\n上限を超えたレコードは読み込まれません。", 1, appname break } c_curr = C_NAME recbuf.ids = strmid(buf, lp, bp - lp) lp = bp ids++ } continue } loop poke recbuf(ids-1), strlen(recbuf(ids-1)) - 2 ; patch 終端ゴミの削除 ; 内容情報 (シンボル名配列・href 参照配列) 更新 ; LB_RESETCONTENT sendmsg objhwnd(id_reclist), 0x184, 0, 0 ; バッファ初期化 sdim symbuf, 64, ids sdim refbuf, 64, ids ; refs(id), refid(id, refs>自動拡張) dim refs, ids dim refid, ids, 2 ; 全 ID の内部情報取得 (2-pass) repeat ids id = cnt gosub *renewref_phase1 loop repeat ids id = cnt gosub *renewref_phase2 loop id = -1 pos_reclist = 0 objprm id_reclist, pos_reclist return ;-------------------------------------------------- ; ID 情報更新 (target = var id) *renewref gosub *renewref_phase1 gosub *renewref_phase2 return ; - - - - - - - - - - - - - - - - - - - - - - - - - *renewref_phase1 ; (target = var id) ; シンボル, リファの更新 symbuf.id = "デフォルトレコード" refbuf.id = "" p = 0 c_curr = C_VOID repeat ; 処理ライン取得 getstr astr, recbuf.id, p if strsize = 0 : break p += strsize a = peek(astr) ; '%' エスケープ (%%hgoehgoe..) if wpeek(astr) = 0x2525 { memcpy astr, astr, strsize + 1, , 1 a = '*' } if a = ';' : continue if a = '%' { ; コメント除去, 小文字化 getstr astr, astr, , ' ' getstr astr, astr, , ';' getstr astr, astr, , 9 CharLower astr c_curr = C_VOID ; フィールドタグ if astr = "%index" { symbuf.id = "" refbuf.id = "" c_curr = C_NAME } else:if astr = "%href" { refbuf.id = "" c_curr = C_HREF } continue } if c_curr = C_NAME { if astr ! "" { symbuf.id = astr c_curr = C_SUMMARY } } else:if c_curr = C_HREF { if astr ! "" { refbuf.id += astr + "\n" } } loop ; UI リスト変更 ; LB_GETTEXT (現在の文字列を取得) sendmsg objhwnd(id_reclist), 0x189, id, varptr(astr) ; 文字列がシンボルと違う場合 更新 if (astr ! symbuf.id) | (symbuf.id = "") { ; LB_DELETESTRING sendmsg objhwnd(id_reclist), 0x182, id, 0 ; LB_INSERTSTRING sendmsg objhwnd(id_reclist), 0x181, id, varptr(symbuf.id) } return ; - - - - - - - - - - - - - - - - - - - - - - - - - *renewref_phase2 ; (target = var id) ; href リンク先をレコード ID に変換 ; str multiline refs -> array refid refs.id = 0 p = 0 repeat ; 処理ライン取得 getstr astr, refbuf.id, p if strsize = 0 : break p += strsize ; 内部リンク f = 0 repeat ids if symbuf.cnt = astr { refid(id, refs.id) = cnt refs.id++ f = 1 } loop ; 外部リンク if f = 0 { refid(id, refs.id) = -1 refs.id++ } loop return ;============================================================ ; misc ;-------------------------------------------------- ; 描画 *redrawer redraw 0 boxpinit ; htheme = stat ; XP テーマフラグ boxp 0, 0, x20, gh ;-------------------------------------------------- ; Right pane Field marker ; バー描画 color 244, 244, 244 : boxf x20+2, , x21-1 color 200, 200, 200 : boxf x21-1, , x21-1 ; マーカー描画 scr = GetScrollPos( objhwnd(id_edit), 1) ; hs コード スクロール位置 #define ctype line2y(%1) ((%1 - scr) * 18 + 11 + y22) h = 0 ; HTML モードフラグ f = 0 ; フィールドタグ フラグ i = 0 ; 文字インデックス p = 0 repeat ; 処理ライン取得 getstr astr, str_edit, p p += strsize if strsize = 0 : break if TYPE_EM_LINEFROMCHAR { ; XP Unicode i += wstrlen(astr) } else { ; ANSI i += strlen(astr) } ; 現在の行の (行末の) y 座標を知る ; EM_LINEFROMCHAR sendmsg objhwnd(id_edit), 0xc9, i, 0 y = line2y(stat) ; タグ行を空行に変換 if (peek(astr) = '^') : if (astr="^") | (astr="^p") | (astr="^P") { poke astr ;= "" } ; フィールドマーカー 描画 (0x2525 -> '%' エスケープ対応) if (peek(astr) = '%') & (wpeek(astr) ! 0x2525) { ; コメント除去, 小文字化 getstr astr, astr, , ' ' getstr astr, astr, , ';' getstr astr, astr, , 9 CharLower astr ; フィールドタグの種類で色分け a = 0 repeat strlen(astr) a = a * 137 + peek(astr, cnt) loop hsvcolor abs(a), 64, 160 ; フィールドマーカー ▽ x = x21 - 4 repeat 6 boxf x-cnt, y-cnt, x-cnt, y+cnt loop f = y h = 0 } else:if (peek(astr) ! ';') * peek(astr) * f { ; バー x = x21 - 8 boxf x-1, f, x, y + 7 ; インライン HTML マーカー 描画 if h = 0 { if astr = "html{" { x = x21 - 1 ; html マーカー ▽ repeat 5 boxf x-cnt, y+cnt-2, x-cnt, y+cnt loop h = y } } else { if astr = "}html" { ; バー x = x21 - 4 boxf x-1, h+2, x, y-2 h = 0 x = x21 - 1 ; html マーカー △ repeat 5 boxf x-cnt, y-cnt, x-cnt, y-cnt+2 loop } else { ; バー x = x21 - 4 boxf x-1, h+2, x, y + 7 } } } i += 2 ; 改行分は 2 index で計算 loop ; マーカーを優先して更新 redraw 1, x20+2, y22+2, x21-x20-4, y29-y22-4 redraw 0 ;-------------------------------------------------- ; Left pane (x10-x19) ; レコード一覧 実サイズを取得 rect = 0, 0, 0, 0 GetWindowRect objhwnd(id_reclist), rect MapWindowPoints 0, hwnd, rect, 2 y18 = rect.3 + 2 ; y18 を決定 ; Left of Left 描画 ; LLBox の rect を定義 rw = x11 - x10 - 2 rh = y18 - y12 - 4 rx = gw - rw ry = 0 rx2 = rx + rw - 1 ry2 = ry + rh - 1 scr = GetScrollPos( objhwnd(id_reclist), 1) ; レコード一覧 スクロール位置 #define ctype id2y(%1) ((%1 - scr) * 12 + 6 + ry) if CFGDATA.CFG_HRBORDERX > 0 { ; 設定された幅がある場合のみ %href view 描画 color 244, 244, 244 : boxf rx, ry, rx2, ry2 color 200, 200, 200 : boxf rx2, ry, rx2, ry2 ; title color 144, 144, 144 font "MS UI Gothic", 12 pos rx + rw/10, id2y(0)-4 if rw >= 60 : mes "%href view" : else:if rw >= 30 : mes "%href" repeat 3 ; config ; default a = ids : b = 0 : mf = 0 : gpm = 1 : exape = 0 ; diff if cnt = 0 { gpcolor 220, 220, 220 ; グレー gpm = 0 } else:if cnt = 1 { gpcolor 60, 120, 240 ; 戻り mf = 1 } else:if cnt = 2 { gpcolor 220 ; 行き a = 1 : b = id : exape = 1 } ; draw ref-streak repeat a, b ip = cnt py = id2y( ip ) outcnt = 0 repeat refs(ip) ip2 = refid(ip, cnt) if mf : if ip2 ! id : continue if ip2 >= 0 { if ip2 = ip { tricorn rx2, py, 3 ; 自己参照 } else { ; 2 点間参照 py2 = id2y( ip2 ) ; 画面領域外の円弧描画は省略 if ((pyry2 & py2>ry2)) = 0 { ; カーブ横幅決定 cw = limit(abs(ip-ip2)*6-2, 0, rw-4) + exape if gpm { gpcircle rx2-cw, py, rx2+cw, py2, 0 } else { circle rx2-cw, py, rx2+cw, py2, 0 } tricorn rx2-cw, (py + py2) / 2, (ip > ip2) * 2 } } } else { if py>ry & py 0) { color 128, 0, 128 top = 0x7fffffff btm = 0x80000000 upcnt = 0 dncnt = 0 repeat ids-1, 1 if instr(symbuf.cnt, 0, str_find) >= 0 { y = id2y(cnt) if top > y : top = y if btm < y : btm = y if y < 0 { if upcnt < 3 : tricorn rx + 3, ry + upcnt*4, 2 upcnt++ } else:if y > rh { if dncnt < 3 : tricorn rx + 3, ry2 - dncnt*4, 0 :else:break dncnt++ } else { x = rx + rw / 2 + 3 boxf rx+3, y, x, y tricorn x, y, 3 } } loop if top ! 0x7fffffff { ; 縦線 boxf rx+3, top, rx+3, btm } } ; terminate LLBox pos x10+2, y12+2 gcopy 0, rx, ry, rw, rh font "MS UI Gothic", 12 color 128, 0, 128 tricorn pos_findlabel(0) + 6, pos_findlabel(1) + 10, 3 pos pos_findlabel(0) + 11, pos_findlabel(1) + 4 mes "シンボル検索" font "MS UI Gothic", 12, 1 syscolor 8 : pos x10+8, y10+8 : mes "レコード一覧" ; BDR_HOGE boxp x10, y10, x19, y12, 0x6 ; BDR_SUNKEN boxp x10, y12, x19, y18, 0xa ;-------------------------------------------------- ; Right pane toolbar ; boxp x20, y20, x29, y22 ; グラデーション box syscolor 20 : r0 = ginfo_r : g0 = ginfo_g : b0 = ginfo_b syscolor 15 : r1 = ginfo_r : g1 = ginfo_g : b1 = ginfo_b a = y22 - y20 repeat a b = a - cnt color (r0 * b + r1 * cnt)/a, (g0 * b + g1 * cnt)/a, (b0 * b + b1 * cnt)/a boxf x20, y20+cnt, x29-1, y20+cnt loop syscolor 8 : pos x20+8, y20+8 : mes "レコード内容" ; BDR_HOGE boxp x20, y20, x29, y22, 0x6 ; BDR_SUNKEN boxp x20, y22, x29, y29, 0xa ;-------------------------------------------------- EnableWindow objhwnd(id_delbtn), (id ! 0) ; [レコード削除] ボタン en/di redraw return ;-------------------------------------------------- ; 編集内容 破棄確認 (OK -> 0, キャンセル -> 1) #defcfunc confirm_break if edited { if cur_file = "" { astr = "無題" } else { astr = cur_file } a = MessageBox(hwnd, "ファイル " + astr + " の内容は変更されています。\n\n変更を保存しますか?", appname, 0x00000033) if a = 2 { ; キャンセル return 1 } else:if a = 6 { ; はい if cur_file = "" { if savefn() = 0 : gosub *savehs } else { gosub *savehs } } } return 0 ;============================================================ ;============================================================ ; event proc #define MOUSEOVER_REFVIEW (mousex>x10 & mousexy12 & mouseyx20 & mousexy22 & mousey=x19 & mousex<=x20) #define MOUSEOVER_HRBORDER (mousex>=x11-5 & mousex<=x11-1 & mousey>y12 & mousey= 80 { y11 += lineh MoveWindow objhwnd(id_insbox), x10+4, y11, x19-x10-a-6, 20, 1 MoveWindow objhwnd(id_insbtn), x19-a, y11, a-4, 20, 1 y11 += lineh + 4 MoveWindow objhwnd(id_find), x10+4, y11, x19-x10-a-6, 20, 1 pos_findlabel = x19-a, y11 } else { a = limit(x19-x10, 0, 90) y11 += lineh MoveWindow objhwnd(id_insbox), x10+4, y11, x19-x10-8, 20, 1 y11 += lineh MoveWindow objhwnd(id_insbtn), x19-a+4, y11, a-8, 20, 1 y11 += lineh + 4 MoveWindow objhwnd(id_find), x10+4, y11, x19-x10-8, 20, 1 y11 += lineh pos_findlabel = x19-a+4, y11 } y12 = y11 + lineh + 2 ; レコード一覧 (エッジ分小さく (margin: 2px 2px 2px 0;)) MoveWindow objhwnd(id_reclist), x11, y12+2, x19-x11-2, y19-y12-4, 1 ; right pane ; フィールドタグ 挿入ボックス / レコード削除ボタン a = limit(x29-x23, 0, 90) if a >= 80 { MoveWindow objhwnd(id_tagbox), x22, y21, x23-x22, 20, 1 MoveWindow objhwnd(id_delbtn), x29-a, y21, a-4, 20, 1 } else { a = limit(x29-x20, 0, 90) y21 += lineh MoveWindow objhwnd(id_tagbox), x20+4, y21, x29-x20-8, 20, 1 y21 += lineh MoveWindow objhwnd(id_delbtn), x29-a+4, y21, a-8, 20, 1 } y22 = y21 + lineh + 2 ; レコード Edit (エッジ分小さく (margin: 2px 2px 2px 0;)) MoveWindow objhwnd(id_edit), x21, y22+2, x29-x21-2, y29-y22-4, 1 ; 描画 gosub *redrawer return ;-------------------------------------------------- *event_WM_COMMAND if lparam = 0 { i = wparam & 0xffff ; CMD ID if (i = CMD_S) & (cur_file = "") : i = CMD_A ; patch if i = CMD_N { ; 新規作成 if confirm_break() = 0 { gosub *nullfn gosub *loadhs } } else:if i = CMD_O { ; 開く if confirm_break() = 0 { if loadfn() = 0 { gosub *loadhs } } } else:if i = CMD_S { ; 上書き保存 if cur_file = "" { if savefn() = 0 : gosub *savehs } else { gosub *savehs } } else:if i = CMD_A { ; 名前をつけて保存 if savefn() = 0 { gosub *savehs } } else:if i = CMD_Q { ; 終了 goto *endapp } else:if i = CMD_V { ; バージョン情報 dialog appname + " " + appver + "\n\nhs 形式ヘルプファイル編集ツール\n\n" + appsig + "\n\nTYPE_EM_LINEFROMCHAR = " + TYPE_EM_LINEFROMCHAR, 0, appname } else:if i = CMD_E { ; 編集メニュー (適当実装) rect = 64, -1 MapWindowPoints hwnd, 0, rect, 1 ; WM_CONTEXTMENU sendmsg objhwnd(id_edit), 0x7b, 0, (rect.1 << 16) | rect.0 } else:if i = CMD_SORT { dialog "すべてのレコードをシンボル名順で並べ替えます。\n\nこの操作は元に戻せません。続行しますか?", 3, appname if stat = 6 { gosub *tool_sort_records edited = 1 } } else:if i = CMD_OPTIM { dialog "すべてのレコードをシンボル名順で並べ替え、さらに関連項目\n (%href) でひもづけられたレコード同士を近くに配置します。\n\nこの処理によって、%href ビューが見やすくなります。\n\nこの操作は元に戻せません。続行しますか?", 3, appname if stat = 6 { gosub *tool_get_href_length len_before = len gosub *tool_optimize_order gosub *tool_get_href_length dialog "最適化が完了しました。\n\n総リンク長 : "+len_before+" -> "+len+" ("+((len-len_before) * 100 / limit(len_before,1,0x7fffffff))+" %)", , appname edited = 1 } } else:if i = CMD_INP { gosub *tool_inport_hsp } else:if i = CMD_IH_STRONG { gosub *tool_ih_strong } else:if i = CMD_IH_TABLE { gosub *tool_ih_table } else:if i = CMD_OPT_SHOWHV { ; ボーダー 関連項目ビュー 表示 でリセット (アニメーション) repeat 10 CFGDATA.CFG_BORDERX = (CFGDATA.CFG_BORDERX + 192) / 2 ; ピクセル CFGDATA.CFG_HRBORDERX = (CFGDATA.CFG_HRBORDERX + 50) / 2 ; パーセント gosub *event_WM_SIZE await loop } else:if i = CMD_OPT_HIDEHV { ; ボーダー 関連項目ビュー 非表示 でリセット (アニメーション) repeat 10 CFGDATA.CFG_BORDERX = (CFGDATA.CFG_BORDERX + 144) / 2 ; ピクセル CFGDATA.CFG_HRBORDERX = (CFGDATA.CFG_HRBORDERX + 0) / 2 ; パーセント if CFGDATA.CFG_HRBORDERX < 10 : CFGDATA.CFG_HRBORDERX = 0 gosub *event_WM_SIZE await loop } return 0 } return ;============================================================ ;============================================================ ; ツール ;-------------------------------------------------- ; レコード削除 (str_edit に直接) *del_rec if id { dialog "レコード " + symbuf.id + " を削除しますか?\n\n(この操作は元に戻せません。)", 3, appname if stat = 6 : str_edit = "" } return ;-------------------------------------------------- ; レコード挿入 (str_edit に直接) *ins_rec if str_insbox = "" { objsel id_insbox return } str_edit += "%index\n" + str_insbox + "\n; 見出し を記入\n\n" str_edit += "%prm\n; パラメータリスト を記入\n; パラメータ説明文 を記入\n\n" str_edit += "%inst\n; 解説文 を記入\n\n" str_edit += "%sample\n; サンプルスクリプト を記入\n\n" str_edit += "%href\n; 関連項目 を記入\n\n" str_edit += "%group\n; グループ を記入\n\n" objprm id_edit, str_edit symbol_select = str_insbox ; あとでこのシンボルを選択させる str_insbox = "" objprm id_insbox, str_insbox return ;-------------------------------------------------- ; インライン HTML 挿入確認 ; return : 0=OK, 1=NG #defcfunc confitm_ih if f_confitm_ih = 0 { dialog "現在のカーソル位置にインライン HTML コードを挿入しますか?\n\nインライン HTML は、解説文 (%inst) でのみ有効です。", 2, appname if stat = 7 : return 1 f_confitm_ih = 1 } return 0 ;-------------------------------------------------- ; インライン HTML *tool_ih_strong if confitm_ih() = 0 { astr = "html{\n" astr += "強調する文章\n" astr += "}html\n" objsel id_edit ; EM_REPLACESEL sendmsg objhwnd(id_edit), 0xc2, 1, varptr(astr) } return ;-------------------------------------------------- ; インライン HTML
*tool_ih_table if confitm_ih() = 0 { astr = "html{\n" astr += "
\n" astr += "\n" astr += "\n" astr += "\n" astr += "
項目 1項目 2
データ 1データ 2
データ 3データ 4
\n" astr += "}html\n" objsel id_edit ; EM_REPLACESEL sendmsg objhwnd(id_edit), 0xc2, 1, varptr(astr) } return ;-------------------------------------------------- ; ヒモの総延長を求める (return : len) *tool_get_href_length len = 0 repeat ids id = cnt repeat refs(id) i = refid(id, cnt) if i >= 0 { len += abs(id - i) } loop loop return ;-------------------------------------------------- ; シンボルソートされた全体バッファ buf 作成 *tool_sort_records ; マージソート len = ids ; ソートする配列長 dim tr, len ; temp arry ; 正規化 symbuf.0 = "" repeat len CharLower symbuf.cnt ar.cnt = cnt loop repeat ; セグメントサイズ定義 n = 1 << cnt ; マージサイズ m = n * 2 ; セグメント サイズ ; 全セグメントに対して repeat ; セグメント 領域定義 p = m * cnt ; セグメント開始点 p1 = p ; パート 1 開始点 e1 = p1 + n ; パート 1 終了点 p2 = e1 ; パート 2 開始点 e2 = limit(p2 + n, 0, len) ; パート 2 終了点 (clipping) s = e2 - p1 ; セグメント サイズ if s <= n : break ; セグメント サイズが閾値以下なら マージしない ; セグメント内 マージ repeat s if p2 >= e2 { ; p2 領域外 tr(cnt) = ar(p1) : p1++ } else:if p1 >= e1 { ; p1 領域外 tr(cnt) = ar(p2) : p2++ } else:if (symbuf(ar(p1)) ! symbuf(ar(p2))) <= 0 { ; 比較 & マージ (strcmp) tr(cnt) = ar(p1) : p1++ } else { tr(cnt) = ar(p2) : p2++ } loop ; マージされた配列をソース配列に貼り付け memcpy ar(p), tr, s * 4 loop ; ソート 完了 if n >= len : break loop ; ソート結果 連結 sdim buf, 1048576 repeat ids i = ar.cnt if strlen(recbuf.i) = 0 : continue ; 末尾改行を強制 if peek(recbuf.i, strlen(recbuf.i)-1) ! 10 { recbuf.i += "\n" } ; 連結 buf += recbuf.i loop gosub *rebuild_recs return ;-------------------------------------------------- ; 並び順の最適化 ; %href リンクで結ばれたレコード同士を近くに配置 *tool_optimize_order ; あらかじめ名前順でソート (これを実行したほうが最適化率が上がる傾向にある) gosub *tool_sort_records id = 0 gosub *redrawer ; 経過を描画 id = -1 ; 数パス実行 repeat 7 ; solve et coagula ; (Phase 1) リンク先のリンク情報に自分へのリンクを追加 (これを実行したほうが最適化率が上がる) repeat ids id = cnt repeat refs(id) i = refid(id, cnt) if i >= 0 { refid(i, refs(i)) = id refs(i)++ } loop loop #if 1 ; (Phase 2) リンク配列を ID 順にソート (これを実行したほうが最適化率が上がる傾向にある) repeat ids id = cnt ; バブルソート (要素数小さい) repeat refs(id) repeat refs(id) - cnt - 1 if refid(id, cnt) > refid(id, cnt+1) { refid(id, cnt) ^= refid(id, cnt+1) refid(id, cnt+1) ^= refid(id, cnt) refid(id, cnt) ^= refid(id, cnt+1) } loop loop loop #else ; (Phase 2) リンク配列を名前順にソート ; シンボル名 正規化 repeat ids CharLower symbuf.cnt loop repeat ids id = cnt ; バブルソート (要素数小さい) repeat refs(id) repeat refs(id) - cnt - 1 if refid(id, cnt) >= 0 & refid(id, cnt+1) >= 0 { if (symbuf(refid(id, cnt)) ! symbuf(refid(id, cnt+1))) > 0 { refid(id, cnt) ^= refid(id, cnt+1) refid(id, cnt+1) ^= refid(id, cnt) refid(id, cnt) ^= refid(id, cnt+1) } } loop loop loop #endif ; (Final) リンク先を近くに寄せながらレコードを連結 #if 0 ; 再帰バージョン (1 pass で収束するが、最適化率低い) if 0 { #deffunc connectbuf int localid ; 連結 ("末尾改行を強制" は、*tool_sort_records で実行済みのため省略) if peek(recbuf.localid) { buf += recbuf.localid recbuf.localid = "" ; 連結済みレコードは消去 } if sublev >= 30 : return repeat refs(localid) if refid(localid, cnt) >= 0 { if peek(recbuf( refid(localid, cnt) )) { connectbuf refid(localid, cnt) } } loop return } buf = "" repeat ids connectbuf cnt loop #else ; 非再帰バージョン (収束に n pass かかるが、最終的な最適化率高い) buf = "" repeat ids id = cnt ; 連結 ("末尾改行を強制" は、*tool_sort_records で実行済みのため省略) if peek(recbuf.id) { buf += recbuf.id recbuf.id = "" ; 連結済みレコードは消去 } repeat refs(id) i = refid(id, cnt) if i >= 0 { if peek(recbuf.i) { buf += recbuf.i recbuf.i = "" ; 連結済みレコードは消去 } } loop loop #endif ; 連結済みレコードを再分解・解析 gosub *rebuild_recs id = 0 gosub *redrawer ; 経過を描画 id = -1 loop return ;-------------------------------------------------- ; hsp ファイルからインポート *tool_inport_hsp dialog "インクルード用 HSP ソーススクリプトから、下記のプリプロセッサで定義された\nシンボル/引数を抽出して追加します。続行しますか?\n\n#deffunc\n#defcfunc\n#func\n#cfunc\n#cmd\n\n(この操作は元に戻せません。)", 3, appname if stat ! 6 : return dialog "hsp;*.as", 16, "インクルード用 HSP3 ソーススクリプト" if stat = 0 : return hspfn = refstr edited = 1 ; fn = hspfn gosub *rebuild_buf exist hspfn sdim hspbuf, strsize + 8 bload hspfn, hspbuf hspfn = getpath(hspfn, 8) ; path を剥く sdim div, 1024, 64 ; (誤動作防止) リテラル文字列加工 p = 0 repeat i = instr(hspbuf, p, "\"") if i = -1 : break p += i b = 0 repeat p++ a = peek(hspbuf, p) if a = 0 : break if (a = '"') & (b ! '\\') { break } if (a <= ' ') | (a = ';') | (a = '/') : poke hspbuf, p, '$' b = a loop if a = 0 : break p++ loop ; /* */ コメント削除 p = 0 repeat i = instr(hspbuf, p, "/*") if i = -1 : break p += i i = instr(hspbuf, p, "*/") if i = -1 : break memcpy hspbuf, hspbuf, strlen(hspbuf)-p-i-1, p, p+i+2 loop ; データ取得 メイン p = 0 repeat ; 処理ライン取得 getstr astr, hspbuf, p if strsize = 0 : break p += strsize ; コメント削除 getstr astr, astr, , ';' getstr astr, astr, , '/' ; split i = 0 ; id n = 0 ; len repeat a = peek(astr, cnt) if (a <= 32) | (a = ',') { if n { div.i = bstr ; store n = 0 ; ignore words CharLower bstr if (bstr ! "ctype") & (bstr ! "global") { i++ } } if a = 0 : break continue } if (a ! '#') & ((i | n) = 0) : break ; プロプロセッサ以外 ignore wpoke bstr, n, a n++ loop if i = 0 : continue CharLower div.0 tmp = "" if (div.0 = "#deffunc") | (div.0 = "#defcfunc") { tmp += "%index\n" + div.1 + "\n; 見出し を記入\n\n" prmlist = "" prmsum = "" a = 2 repeat if a >= i : break if prmlist ! "" : prmlist += ", " prmlist += div(a+1) prmsum += div(a+1) + "\t: (" + div(a) + ")\n" a += 2 loop ; ctype if div.0 = "#defcfunc" { prmlist = "(" + prmlist + ")" } tmp += "%prm\n" + prmlist + "\n" + prmsum + "\n" } else:if (div.0 = "#func") | (div.0 = "#cfunc") { tmp += "%index\n" + div.1 + "\n; 見出し を記入\n\n" prmlist = "" prmsum = "" a = 3 repeat if a >= i : break if prmlist ! "" : prmlist += ", " prmlist += div(a) + (cnt+1) prmsum += div(a) + (cnt+1) + "\t: \n" a++ loop ; ctype if div.0 = "#cfunc" { prmlist = "(" + prmlist + ")" } tmp += "%prm\n" + prmlist + "\n" + prmsum + "\n" } else:if div.0 = "#cmd" { tmp += "%index\n" + div.1 + "\n; 見出し を記入\n\n" str_edit += "%prm\n; パラメータリスト を記入\n; パラメータ説明文 を記入\n\n" } if tmp ! "" { tmp += "%inst\n; 解説文 を記入\n\n" tmp += "%sample\n; サンプルスクリプト を記入\n\n" tmp += "%href\n; 関連項目 を記入\n\n" tmp += "%group\n; グループ を記入\n\n" tmp += "; 定義 : [" + hspfn + "] " + astr + "\n\n\n" buf += tmp } loop gosub *rebuild_recs return ;============================================================ ;============================================================ ; 終了シーケンス *endapp if confirm_break() { return } else { timer_stop oncmd 0 chdir dir_exe Window_save CFGFILE, CFGDATA end end } #global #if HHW_DEBUG hhx_edit_start "HHW", "1.1", "S.Programs 2006-2007", dir_cmdline #endif