editboxstream

何かと便利なエディットボックスストリーム。

//
// editbox_stream.hpp
//

#ifndef KN_EDITBOX_STRAEM_HPP__
#define KN_EDITBOX_STRAEM_HPP__

#include 

#include 
#include 
#include 

namespace kn {

struct editbox_wrapper
{
    static std::map wndproctbl_;
    HWND handle_;
    WNDPROC prev_wndproc_;
    bool is_modified_;

    // コールバック
    static LRESULT editbox_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

    // ctor & dtor
    editbox_wrapper(HWND handle)
            : handle_(handle)
            , prev_wndproc_(NULL)
            , is_modified_(true)
    {
    }

    ~editbox_wrapper()
    {
        stop_hook();
    }

    // 更新チェック
    bool is_modified()
    {
        return is_modified_;
    }

    // フック開始
    void start_hook();

    // フック停止
    void stop_hook();

    // リロード
    template 
    DWORD reload_text(Container& buf)
    {
        DWORD len = (DWORD)::SendMessage(handle_, WM_GETTEXTLENGTH, (WPARAM)0, (LPARAM)0);
        buf.resize(len);
        len = (DWORD)::SendMessage(handle_, WM_GETTEXT, (WPARAM)len, (LPARAM)&buf[0]);
        is_modified_ = false;
        return len;
    }
};

//
// class basic_editbox_streambuf
//
//     stream buffer for the EditBox stream
//
template<
    typename charT,
    typename Traits = std::char_traits
>
class basic_editbox_streambuf
    : public std::basic_streambuf
    , public editbox_wrapper
{
private:
    typedef basic_editbox_streambuf this_type;
public:
    typedef Traits traits_type;
    typedef typename std::basic_streambuf::char_type char_type;
    typedef typename std::basic_streambuf::int_type int_type;
    typedef typename std::basic_streambuf::off_type off_type;
    typedef typename std::basic_streambuf::pos_type pos_type;
    typedef int_type streamsize;

private:
    std::vector pbuf_;
    std::vector gbuf_;

public:
    // sync
    //     バッファのフラッシュ用らしい
    virtual int sync()
    {
        overflow(traits_type::eof());
        return 0;
    }

    // overflow
    //     オーバーフローの発生時に呼ばれる(つまり書き込みバッファが埋まったとき)
    virtual int_type overflow(int_type c)
    {
        char_type* p = pbase();
        *(p + (pptr() - pbase())) = traits_type::to_char_type(0);
        ::SendMessage(handle_, EM_SETSEL, (WPARAM)-1, (LPARAM)-1);
        ::SendMessage(handle_, EM_REPLACESEL, (WPARAM)TRUE, (LPARAM)p);

        pbump(pbase() - pptr());
        if (c != traits_type::eof()) {
            *pbase() = c;
            pbump(1);
        }
        return traits_type::not_eof(c);
    }

    // underflow
    //     アンダーフロー発生時に呼ばれる(つまり読み込みバッファが空になったとき)
    virtual int_type underflow()
    {
        // 更新チェック
        if (is_modified()) {
            // テキストを読み込み直す
            DWORD len = reload_text(gbuf_);
            // バッファの再登録
            setg(&gbuf_[0], &gbuf_[0] + (gptr() - eback()), &gbuf_[0] + len);
            if (gptr() < egptr()) {
                int_type c = *gptr();
                return traits_type::not_eof(c);
            } else {
                return traits_type::eof();
            }
        } else {
            if (gptr() < egptr()) {
                int_type c = *gptr();
                gbump(1);
                return traits_type::not_eof(c);
            } else {
                return traits_type::eof();
            }
        }
    }

    // xsputn
    virtual streamsize xsputn(const char_type* s, streamsize n)
    {
        streamsize total_put_size = 0;
        do {
            streamsize bufmax = epptr() - pptr();
            streamsize putsize = (bufmax < n) ? bufmax : n;
            char_type* p = pptr();
            memcpy(p, s, putsize);
            total_put_size += putsize;
            pbump(putsize);
            n -= putsize;
        } while (n > 0 && overflow(*s++) != traits_type::eof());
        return total_put_size;
    }

public:
    basic_editbox_streambuf(HWND handle = NULL)
        : editbox_wrapper(handle)
    {
        pbuf_.resize(1024);
        gbuf_.resize(1);
        setp(&pbuf_[0], &*pbuf_.end());
        setg(&gbuf_[0], &gbuf_[0], &gbuf_[0]);
    }

    ~basic_editbox_streambuf()
    {
        sync();
        stop_hook();
    }

    void attach(HWND handle)
    {
        stop_hook();
        handle_ = handle;
        setp(&pbuf_[0], &*pbuf_.end());
        setg(&gbuf_[0], &gbuf_[0], &gbuf_[0]);
    }

    HWND get_handle() const
    {
        return handle_;
    }
};

typedef basic_editbox_streambuf editbox_streambuf;
typedef basic_editbox_streambuf weditbox_streambuf;

template
struct editbox_stream_implement_helper
    : public BaseT
{
protected:
    typedef editbox_stream_implement_helper helper_type;

private:
    basic_editbox_streambuf sb;

protected:
    basic_editbox_streambuf* rdbuf()
    {
        return &sb;
    }

public:
    explicit editbox_stream_implement_helper(HWND handle)
        : BaseT(&sb)
        , sb(handle)
    {
    }

    void attach(HWND handle) { rdbuf()->attach(handle); }
};

template<
    typename charT,
    typename Traits = std::char_traits
>
class basic_ieditbox_stream
    : public editbox_stream_implement_helper<
        charT, Traits, std::basic_istream >
{
public:
    explicit basic_ieditbox_stream(HWND handle = NULL)
        : helper_type(handle)
    {
    }
};

template<
    typename charT,
    typename Traits = std::char_traits
>
class basic_oeditbox_stream
    : public editbox_stream_implement_helper<
        charT, Traits, std::basic_ostream >
{
public:
    explicit basic_oeditbox_stream(HWND handle = NULL)
        : helper_type(handle)
    {
    }
};

template<
    typename charT,
    typename Traits = std::char_traits
>
class basic_editbox_stream
    : public editbox_stream_implement_helper<
        charT, Traits, std::basic_iostream >
{
public:
    explicit basic_editbox_stream(HWND handle = NULL)
        : helper_type(handle)
    {
    }
};

typedef basic_ieditbox_stream ieditbox_stream;
typedef basic_ieditbox_stream wieditbox_stream;
typedef basic_oeditbox_stream oeditbox_stream;
typedef basic_oeditbox_stream woeditbox_stream;
typedef basic_editbox_stream editbox_stream;
typedef basic_editbox_stream weditbox_stream;

} // end of namespace kn

#ifdef KN_EDITBOX_STRAEM_IMPLEMENT
#include "./editbox_stream.cpp"
#endif

#endif /* KN_EDITBOX_STRAEM_HPP__ */

//
// editbox_stream.cpp
//

#undef KN_EDITBOX_STRAEM_IMPLEMENT
#include "./editbox_stream.hpp"

namespace kn {

std::map editbox_wrapper::wndproctbl_;

// コールバック
LRESULT editbox_wrapper::editbox_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    std::map::const_iterator itr = wndproctbl_.find*1 {
        editbox_wrapper* self = itr->second;
        switch (msg) {
        case WM_COMMAND:
            switch (HIWORD(wparam)) {
            case EN_CHANGE:                     
                self->is_modified_ = true;
                break;
            }
            break;
        }
        return CallWindowProc(self->prev_wndproc_, hwnd, msg, wparam, lparam);
    }
    // must not pass through here
    ASSERT(0);
    return 0;
}

// フック開始
void editbox_wrapper::start_hook()
{
    if (prev_wndproc_ == NULL) {
        wndproctbl_[handle_] = this;
        prev_wndproc_ = (WNDPROC)SetWindowLong(handle_, GWL_WNDPROC, (LONG)&editbox_wndproc);
    }
}

// フック停止
void editbox_wrapper::stop_hook()
{
    if (prev_wndproc_ != NULL) {
        SetWindowLong(handle_, GWL_WNDPROC, (LONG)prev_wndproc_);
        wndproctbl_.erase(handle_);
        prev_wndproc_ = NULL;
    }
}

} // end of namespace kn

*1:HWND)hwnd); if (itr != wndproctbl_.end(