2008年2月12日火曜日

アンドゥや再生ができるペイントツール

GoFのCommandパターンにヒントを得て作成。
描画した履歴を文字列型配列変数に記録しておき、必要に応じて取り出します。
今回は単純な文字列型配列変数ではなく、スタックのモジュールを用意してみました。

bregexp.dll(bregonig.dll)および月影ともさんのbregexp.hspが必要です。
// 文字列用スタック
#module string_stack stack, max
#modinit
    max = 0
    sdim stack, 3210
    return
#deffunc new_sstack array v
    newmod v, string_stack@
    return
#modfunc push str s
    stack(max) = s
    max++
    return
#defcfunc pop modvar string_stack@
    if max == 0 {
        logmes "引数の値が異常です。"
        return ""
    }
    max--
    return stack(max)
#defcfunc get_length modvar string_stack@
    return max
#defcfunc get_last modvar string_stack@
    return stack(max-1)
#defcfunc get_at modvar string_stack@, int index
    if index < 0 || max <= index {
        logmes "引数の値が異常です。"
        return ""
    }
    return stack(index)
#modfunc clear_stack
    max = 0
    return
#global

// 矩形の塗りつぶし
// http://rpen.blogspot.com/2007/11/blog-post.html
#include "gdi32.as"
#module
#const FLOODFILLSURFACE 1
#deffunc fill int x, int y
    current_color = ginfo_rginfo_gginfo_b
    CreateSolidBrush (ginfo_b << 16) | (ginfo_g << 8) | ginfo_r
    if stat {
        hBrush = stat
    } else {
        dialog "ブラシの生成に失敗しました。プログラムを終了します。"1
        end
    }
    SelectObject hDC, hBrush
    pget x, y
    ExtFloodFill hdc, x, y, (ginfo_b << 16) | (ginfo_g << 8) | ginfo_rFLOODFILLSURFACE
    DeleteObject hBrush
    color current_color(0), current_color(1), current_color(2)
    return
#global

// 命令を解析して描画するモジュール
#include "bregexp.hsp"
#module drawer
#define ctype result(%1int(_result(%1))
#deffunc draw str _cmd
    cmd = _cmd
    BSplit _result, cmd, "m/[ ,]+/"
    switch _result(0)
        case "moveTo" : pos result(1), result(2) : swbreak
        case "lineTo" : line result(1), result(2) : swbreak
        case "color"  : color result(1), result(2), result(3) : swbreak
        case "fill"   : fill result(1), result(2) : redraw 1 : swbreak
        default : logmes "未知の命令です" : swbreak
    swend
    return
#deffunc draw_all array cmds, int wait_time
    redraw 0
    color 255255255 : boxf
    color
    repeat get_length(cmds)
        draw get_at(cmds, cnt)
        if wait_time {
            redraw 1
            wait wait_time
            redraw 0
        }
    loop
    redraw 1
    return
#global

#define push_and_do(%1,%2push %1%2 : draw %2

#define WM_MOUSEMOVE    $00000200
#define WM_LBUTTONDOWN  $00000201
#define WM_LBUTTONUP    $00000202
#define WM_RBUTTONDOWN  $00000204

*init
    title "左ドラッグで線を描画 / 右クリックで塗りつぶし"
    oncmd gosub *onLButtonDownWM_LBUTTONDOWN
    oncmd gosub *onRButtonDownWM_RBUTTONDOWN
    oncmd gosub *onLButtonUpWM_LBUTTONUP
    oncmd gosub *onMouseMoveWM_MOUSEMOVE

    objsize 80
    button gosub "color change"*color_change
    button gosub "redraw slowly"*all_draw_slowly
    button gosub "clear"*clear
    button gosub "undo"*undo
    new_sstack cmds
    stop

// 色の変更
*color_change
    hsvcolor rnd(192), 255255
    push_and_do cmds, "color " + ginfo_r + "," + ginfo_g + "," + ginfo_b
    return
// 全消去
*clear
    clear_stack cmds
    draw_all cmds, 0
    return
// アンドゥ
*undo
    tmp = pop(cmds)
    draw_all cmds, 0
    return
// すべて描画
*all_draw
    draw_all cmds, 0
    return
// ゆっくりとすべて描画
*all_draw_slowly
    oncmd 0
    gosub *invalidate_buttons
    draw_all cmds, 4
    gosub *validate_buttons
    oncmd 1
    return
// 左ドラッグ開始
*onLButtonDown
    dragging = 1
    push_and_do cmds, "moveTo " + mousex + "," + mousey
    return
// 左ドラッグ終了
*onLButtonUp
    dragging = 0
    return
// 左ドラッグ中
*onMouseMove
    if dragging {
        push_and_do cmds, "lineTo " + mousex + "," + mousey
    }
    return
// 右クリック
*onRButtonDown
    if dragging == 0 {
        push_and_do cmds, "fill " + mousex + "," + mousey
    }
    return

#include "obj.as"
*invalidate_buttons
    repeat 4
        objgray cnt0
    loop
    return
*validate_buttons
    repeat 4
        objgray cnt1
    loop
    return

2008年2月9日土曜日

リストビューのソート

リストビューのアイテムをLVM_SORTITEMSEXメッセージを使ってソートします。ちょくとさんのコールバック関数DLL「hscallbk.dll」が必要です。
リストビューのモジュールとしてもそこそこ利用できるかもしれません。

エクスプローラのように、「ヘッダ部分をクリックすると並び変わる」ようにもできるでしょう。(参考:http://hsp.tv/play/pforum.php?mode=pastwch&num=2749


LVM_SORTITEMSEXメッセージの日本語情報は意外と少ないので、気が向いたら開発Wikiにフィードバックします
// 参考資料:
//      リストビューを作成してみる
//          http://yokohama.cool.ne.jp/chokuto/urawaza/listview1.html
//      Windows32 API Constance 検索
//          http://hspnext.com/tool/hsptool04.htm
//      MSDN - LVM_SORTITEMSEX
//          http://msdn2.microsoft.com/ja-jp/library/bb761055(en-us).aspx
#module mod_listview
#include "hscallbk.as"
#uselib ""
#func sort_items "" int, int, int

#define LVM_SETITEM             $00001006
#define LVM_INSERTITEM          $00001007
#define LVM_INSERTCOLUMN        $0000101B
#define LVM_SORTITEMSEX         $00001051
#define LVM_GETITEMTEXTA        $0000102D
#define LVS_REPORT              $00000001
#define WS_EX_NOPARENTNOTIFY    $00000004
#define WS_VISIBLE              $10000000
#define WS_CHILD                $40000000

#deffunc make_listview
    if vartype(proc) != vartype("callback") : gosub *init
    winobj "SysListView32""ListView"WS_EX_NOPARENTNOTIFYWS_VISIBLE | WS_CHILD | LVS_REPORT, -1, -1
    return stat
*init
    setcallbk proc, sort_items*sort_flag
    sdim name, 642
    dim lvcolumn, 8
    dim lvitem, 6
    lvcolumn.0 = 0x000F
    lvcolumn.2 = 100
    lvitem.0 = 0x0001
    lvitem.6 = 64
    return

#deffunc add_column int id_list, str column_name, int index
    if(index < 0 | id_list < 0) {
        logmes "パラメータが不正です。"
        return -1
    }
    name = column_name
    lvcolumn.3 = varptr(name)
    sendmsg objinfo_hwnd(id_list), LVM_INSERTCOLUMN, index, varptr(lvcolumn)
    return stat

#deffunc add_item int id_list, array item, int index
    if(index < 0 | id_list < 0) {
        logmes "パラメータが不正です。"
        return -1
    }
    if vartype(item) != vartype("str") {
        logmes "配列変数の型が不正です。文字列型の変数を渡してください。"
        return -1
    }
    // アイテムの作成
    lvitem.1 = index
    lvitem.2 = 0
    lvitem.5 = varptr(item)
    sendmsg objinfo_hwnd(id_list), LVM_INSERTITEM0varptr(lvitem)

    // サブアイテムの作成
    repeat length(item) - 11
        lvitem.2 = cnt
        lvitem.5 = varptr(item(cnt))
        sendmsg objinfo_hwnd(id_list), LVM_SETITEM0varptr(lvitem)
    loop
    return stat

#deffunc sort int id_list, int column, int vtype, int sortmode
    if(column < 0 | id_list < 0 | vtype < 0) {
        logmes "パラメータが不正です。"
        return -1
    }
    lvitem.2 = column
    var_type = vtype
    sendmsg objinfo_hwnd(id_list), LVM_SORTITEMSEX, sortmode, varptr(proc)
    return

#defcfunc local compareAsInt int id_list, int index1, int index2, int sortmode
    gosub *startCompare
    return int(name(sortmode & 1)) - int(name((sortmode & 1) ^ 1))

#defcfunc local compareAsStr int id_list, int index1, int index2, int sortmode
    gosub *startCompare
    return name(sortmode & 1) ! name((sortmode & 1) ^ 1)
    return

*startCompare
    lvitem.5 = varptr(name(0))
    sendmsg objinfo_hwnd(id_list), LVM_GETITEMTEXTA, index1, varptr(lvitem)
    lvitem.5 = varptr(name(1))
    sendmsg objinfo_hwnd(id_list), LVM_GETITEMTEXTA, index2, varptr(lvitem)
    return

// サブアイテム(ファイルサイズ)でソート
// 第3引数が0なら昇順、1なら降順
*sort_flag
    if var_type == vartype("int") {
        return compareAsInt@mod_listview(id_list, callbkarg(0), callbkarg(1), callbkarg(2))
    } else : if var_type == vartype("str") {
        return compareAsStr@mod_listview(id_list, callbkarg(0), callbkarg(1), callbkarg(2))
    }
    return 0

#global // end of mod_listview


// 疑似的な「ファイル」の数
#define NUM_FILES   10

    randomize
    gosub *createGuiObjects
    stop

// ボタンクリックによって呼び出されるサブルーチン
*sort_asc_by_name
    sort id_list, 0vartype("str"), 0
    return
*sort_desc_by_name
    sort id_list, 0vartype("str"), 1
    return
*sort_asc_by_size
    sort id_list, 1vartype("int"), 0
    return
*sort_desc_by_size
    sort id_list, 1vartype("int"), 1
    return

// ボタンとリストビューの作成
*createGuiObjects
    // ボタンを作成
    objsize ginfo_winx / 432
    pos 00 : button gosub "ファイル名で昇順にソート"*sort_asc_by_name
    pos ginfo_winx / 40 : button gosub "ファイル名で降順にソート"*sort_desc_by_name
    pos ginfo_winx / 20 : button gosub "ファイルサイズで昇順にソート"*sort_asc_by_size
    pos ginfo_winx * 3 / 40 : button gosub "ファイルサイズで降順にソート"*sort_desc_by_size

    // リストビューコントロール作成
    pos 032 : objsize ginfo_winxginfo_winy - 32
    make_listview : id_list = stat

    // カラムを追加
    column_name = "ファイル名""ファイルサイズ"
    repeat 2
        add_column id_list, column_name(cnt), cnt
        if stat == -1 {
            dialog "カラムの追加に失敗"1
            end
        }
    loop

    // アイテム・サブアイテムの追加
    sdim item_name, 642
    repeat NUM_FILES
        item_name = "ファイル" + cnt"" + rnd(1000) + " KB"
        add_item id_list, item_name, cnt
        if stat == -1 {
            dialog "アイテムの追加に失敗"1
            end
        }
    loop
    return

2008年2月5日火曜日

正規表現でHTMLの見出しを抽出する

bregonig.dllを使いたくて「ヘッダファイル作ろうかなー」と考えていたら既に月影ともさん(と猫太さん)が作成されていました。
COMによる正規表現は何かと不便なので、今後重宝しそうです。
リンク先を変更。[08/05/03]

// つーさのくーかん「物置 > HSP3 > BREGEXP.hsp」
// http://tu-sa.net/0360

#runtime "hsp3cl"
#include "bregexp.hsp"
    dialog "htm;*.html"16"見出しを抽出するHTMLファイル"
    if stat == 0 : end

    notesel file
    noteload refstr
    position = 0
    margin_left = "-""--""---""----""-----""------"
    repeat
        result = BMatch(file, position, "m#<[hH]([1-6])[^>]*>(.*)</[hH]\\1>#k")
        if result == -1 : break

        mes margin_left(int(BMGetStr(1))-1) + BMGetStr(2)
        position += BMGetNextPos()
    loop
    stop