004-05  前へ←  ホームへ  →次へ  004-07

No.004 - 06  ダブルクリックでドッキング←→フローティング切替


ダブルクリックでフローティング・ドッキング切り替えをします。


ドッキング
しているツールバーのつまみ部分をダブルクリックすると、フローティングします。
フローティングしているツールバーのタイトルバーをダブルクリックすると、ドッキングします。

これを実現するためには フローティング&ドッキング位置を保持 しておいて、ダブルクリックされたときに元の位置に フローティングorドッキングさせればいいですね。・・・ですが、この仕様を実装するの意外と手間です。
というよりかドラッグ検知でダブルクリックを回避するのがちょっと大変なのです。

・怖い怖いダブルクリック
ドラッグ検知でダブルクリックを回避するのはちょっと大変です。

当然の問題としてダブルクリックはWM_LBUTTONDON, UP が2回ずつ発生します。
今までのプログラムはWM_LBUTTONDON はでデスクトップDCをロックし、WM_LBUTTONUP で解放していました。
実はある程度高速に連続クリックされるとフリーズします。
(マシンスペックにもよりますが、いいマシンほど高解像なのでやっぱフリーズ率は大です)
これではダメ仕様ですね。

仕方がないのでタイマを使います。
WM_LBUTTONDOWNSetTimer() してダブルクリック時間(GetDoubleClickTime()で取得)過ぎたらドラッグ開始
WM_LBUTTONUPKillTimer() します。ダブルクリック時間前 にマウス左ボタンが押上されたらドラッグ・キャンセルです。

ただタイマだけでは操作速度的に使いづらいです。
WM_LBUTTONDON の後でダブルクリックのマウス移動の許容範囲越えて移動した時もドラッグ開始します。
ダブルクリックの許容範囲はGetSystemMetrics(SM_CXDOUBLECLK), GetSystemMetrics(SM_CYDOUBLECLK)で取得します。
さらにシフトキー・コントロールキーが押された時もドラッグ開始します。

WM_LBUTTONDON ではツールバーに対してマウス左ボタンが押されたことを示すフラグ m_dock_context.m_pre_started をセットしています。マウス左ボタンが押されたままツールバーの上をマウスが移動した場合に、不正処理されないようにしています。


ダブルクリックを回避するドラッグ検知の流れ
WM_LBUTTONDOWN
<ドラッグ・準備>
ダブルクリック受付時間
を過ぎた
ダブルクリックの
許容移動範囲を越えて移動した
WM_LBUTTONUP
<ドラッグ・キャンセル>
<ドラッグ・開始>
WM_LBUTTONUP
<ドラッグ終了>




・ダブルクリックでフローティング&ドッキング切り替え
ダブルクリックでドッキング・フローティングを切り替えるのはそれぞれ位置を保持しておいてやれば簡単です。
以下のソースではフローティング位置をPOINT m_float_posメンバに、
ドッキング位置を親ドックバーID、行の番号、親ウィンドウからのオフセット位置を
int m_def_parent_dock_id, m_def_dock_row_num, m_def_dock_offset;
の各変数に分けています。


ダブルクリック


docking_test6.cpp
#define _WIN32_IE 0x0501
#include <windows.h>
#include <commctrl.h>
#include "plist.h"

// カスタムFillRect(矩形塗りつぶし)
FillRect(HDC hdc, int left, int top, int right, int bottom, HBRUSH hbr){
    RECT rc;
    rc.left   = left;
    rc.top    = top;
    rc.right  = right;
    rc.bottom = bottom;
    FillRect(hdc, &rc, hbr);
}
// クラスの仮宣言
class DOCK_SYS;

// ツールバークラス
class TOOL_BAR {
    public:
    HWND m_hwnd, m_control_tool_bar, m_popup_frame; // 各HWND
    DOCK_SYS *m_p_dock_sys;     // ドッキング・ツールバー統括クラス・ポインタ
    int m_floating_flag;    // フローティング・フラグ  浮いてれば==1  そうでないなら==0
    int m_vh_type, m_parent_dock_id;
    SIZE *m_p_size, m_horz_size, m_vert_size;
    int m_thick, m_length;
    POINT m_float_pos;  // フロート位置
    int m_def_parent_dock_id, m_def_dock_row_num, m_def_dock_offset;

    create(DOCK_SYS *p_dock_sys, TBBUTTON *tb_buttons, int tb_button_len);  // ツールバー生成
    void set_horz();    // ツールバー・横型に変更
    void set_vert();    // ツールバー・縦型に変更
    void renew_size_var();  // サイズ変数の更新
    void renew_size();  // 実際のサイズ更新

     // ポップアップ・フレーム・プロシージャ
    PopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
    static LRESULT CALLBACK StaticPopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);

     // ツールバー・プロシージャ
    ToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
    static LRESULT CALLBACK StaticToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
};
// ドック・コンテキスト・クラス
class DOCK_CONTEXT {
    public:
    HRGN m_apply_rgn, m_work_rgn, m_work_rgn2, m_last_rgn, m_target_rgn;    // リージョン
    HDC m_desk_hdc;         // デスクトップDC
    int m_now_dragging;     // ドラッグ中なら==1  そうでないなら==0
    int m_pre_started;
    SIZE m_drag_size;       // ドラッグ中の矩形サイズ
    SIZE m_doubleclk_size;  // ダブルクリック・許容範囲
    POINT m_cursor_offset;  // ドラッグ中の矩形位置のマウス位置との差分
    POINT m_start_cursor_pos; // ドラッグ開始時のマウスカーソル位置
    HBRUSH m_desk_old_brush;    // デスクトップDC・ブラシ
    HBRUSH m_half_tone_brush;   // ハーフトーン・ブラシ
    TOOL_BAR *m_p_tool_bar; // ドラッグ中のツールバー

    DOCK_CONTEXT();     // コンストラクタ
    ~DOCK_CONTEXT();    // デストラクタ

    void pre_start_drag(TOOL_BAR *ptool);   // ドラッグ開始の前準備
    void start_drag(); // ドラッグ開始
    void drag(RECT target_rc, int target_rc_fill, int flip);        // ドラッグ中
    void end_drag();    // ドラッグ終了
    void get_drag_rect(RECT *p_drag_rc);    // ドラッグ矩形の取得
};
class DOCK_BAR_ROW {
    public:
    XLIST<TOOL_BAR*> m_ptool_list;
    XLIST<int> m_tool_offset_list;
    int m_thick;
};

// ドックバークラス
class DOCK_BAR {
    public:
    enum { ID_TOP, ID_LEFT, ID_BOTTOM, ID_RIGHT, ID_DOCK_BAR_SIZE };
    HWND m_hwnd;
    int m_vh_type;  // 縦なら=='V' 横なら=='H'
    int m_pos_id;   // 位置ID(==ID_TOP or ID_LEFT or ID_BOTTOM or ID_RIGHT)
    int m_thick, m_length;
    int m_front_co, m_roof_co;
    PLIST<DOCK_BAR_ROW> m_row_list;

    // ドックバー・プロシージャ
    DOCK_BAR::DockBarProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
    // ドックバー・プロシージャ(static版)
    static LRESULT CALLBACK DOCK_BAR::StaticDockProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);

    void create(HWND main_hwnd, int pos_id);
    void add_tool_bar(TOOL_BAR *ptool, int row_num, int offset, int insert);    // ツールバーを合加する
    int remove_tool_bar(TOOL_BAR *ptool);   // ツールバーを放離する

    // ドラッグによるドッキング位置取得
    void get_dock_pos_by_drag(RECT *p_drag_rc, TOOL_BAR *ptool, int *p_row_num, int *p_offset, int *p_insert);
    int get_dock_row_num_by_drag(RECT *p_drag_rc, int *p_insert);
    int get_dock_offset(int aim_front, TOOL_BAR *ptool, int *p_row_num, int *p_insert);

    // 引数 end_line を基準線として tool_num 番目までのツールバーを「左」「右」に寄せる
    void shift_left_tool_bar(int row_num, int tool_num, int end_line);
    void shift_right_tool_bar(int row_num, int tool_num, int end_line);
    int get_total_space(int row_num);

    void tidy_row();    // ツールバーがあれば行の"厚さ変数"を更新、無ければ行を削除
    void renew_inside();    // "厚さ変数" と内部ツールバー位置を更新する
    void set_length(int length);    // 長さ変数の設定+ツールバー位置補正
};

// ドッキング・ツールバー統括クラス
class DOCK_SYS {
    public:
    HWND m_main_frame, m_hview;     // 各HWND
    int m_now_nc_activing;          // WM_NCACTIVATEフラグ WM_NCACTIVATEの多重再送を防ぐ
    DOCK_CONTEXT m_dock_context;    // ドック・コンテキスト・構造体

    DOCK_BAR m_dock_bar[4]; // ドックバークラス
    DOCK_BAR *m_prio_dock_bar[4];   // ドックバー優先順位;
    XLIST<TOOL_BAR*> m_ptool_list;

    ~DOCK_SYS();    // デストラクタ
    regist_winc(char *caption, HBRUSH hbr_bg, WNDPROC proc);    // ウィンドウクラス登録
    create(HWND main_hwnd, HWND child_view_hwnd);   // 生成
    void on_ncactivate(HWND hwnd, WPARAM wp);       // WM_NCACTIVATE 処理
    void recalc_layout();           // 子ウィンドウ位置再計算
    void tool_on_lbuttondown(TOOL_BAR *ptool); // ツールバー・マウス左ボタン押下
    void tool_on_lbuttonup();            // ツールバー・マウス左ボタン押上
    void tool_on_mousemove();            // ツールバー・マウス移動
    void tool_drag();   // ツールバー・ドラッグ
    void tool_start_drag(); // ツールバー・ドラッグ・スタート
    void tool_on_keydown(WPARAM wp, LPARAM lp); // ツールバー・キー押下
    void tool_on_keyup(WPARAM wp, LPARAM lp);   // ツールバー・キー押上
    void create_tool_bar(TBBUTTON *tb_buttons, int tb_button_len);  // ツールバー生成
    get_dock_by_drag(RECT *p_drag_rc, int now_dock_id, int *p_flip);    // ドラッグの時のドッキング先取得

    void do_dock(TOOL_BAR *ptool, int dock_id);
    void do_dock(TOOL_BAR *ptool, int dock_id, int row_num, int offset, int insert);
    void do_float(TOOL_BAR *ptool, int x, int y, int flip); // フローティング実行
} ;

// コンストラクタ
DOCK_CONTEXT::DOCK_CONTEXT(){
    WORD gray_pattern[8];
    HBITMAP gray_bitmap;
    int i;

    m_pre_started=0;
    m_now_dragging=0;
    m_apply_rgn=m_work_rgn=m_work_rgn2=m_last_rgn=m_target_rgn=NULL;    // リージョン
    m_desk_hdc=NULL;        // デスクトップDC
    m_drag_size.cx=m_drag_size.cy=m_doubleclk_size.cx=m_doubleclk_size.cy=0;
    m_cursor_offset.x=m_cursor_offset.y=m_start_cursor_pos.x=m_start_cursor_pos.y=0;
    m_desk_old_brush=m_half_tone_brush=NULL;
    m_p_tool_bar=NULL;  // ドラッグ中のツールバー

    m_doubleclk_size.cx =  GetSystemMetrics(SM_CXDOUBLECLK);
    m_doubleclk_size.cy =  GetSystemMetrics(SM_CYDOUBLECLK);

    // ハーフトーン・パレット生成
    for (i=0;i<8;i++) gray_pattern[i] = (WORD)(0x5555 << (i & 1));  // 0x5555 は2bitで  01010101 01010101
    gray_bitmap = CreateBitmap(8, 8, 1, 1, &gray_pattern);
    if (gray_bitmap != NULL) {
        m_half_tone_brush = ::CreatePatternBrush(gray_bitmap);
        DeleteObject(gray_bitmap);
    }
}
// デストラクタ
DOCK_CONTEXT::~DOCK_CONTEXT(){
    DeleteObject(m_half_tone_brush); // ハーフトーン・パレット破棄
}
// ドラッグ矩形の取得
void DOCK_CONTEXT::get_drag_rect(RECT *p_drag_rc){
    POINT po;
    GetCursorPos(&po);
    p_drag_rc->left=po.x-m_cursor_offset.x;
    p_drag_rc->top =po.y-m_cursor_offset.y;
    p_drag_rc->right = p_drag_rc->left + m_drag_size.cx;
    p_drag_rc->bottom= p_drag_rc->top  + m_drag_size.cy;
}
// ドラッグ開始の前準備
void DOCK_CONTEXT::pre_start_drag(TOOL_BAR *ptool){
    m_p_tool_bar = ptool;
    GetCursorPos(&m_start_cursor_pos);
    HWND hwnd = (m_p_tool_bar->m_floating_flag)? m_p_tool_bar->m_popup_frame : m_p_tool_bar->m_hwnd;
    SetCapture(hwnd);
}
// ドラッグ開始
void DOCK_CONTEXT::start_drag(){
    HWND desk_hwnd;
    RECT rc;
    m_now_dragging=1;
    HWND hwnd = (m_p_tool_bar->m_floating_flag)? m_p_tool_bar->m_popup_frame : m_p_tool_bar->m_hwnd;

    // ドラッグ・サイズとマウスとの座標差
    GetWindowRect(hwnd, &rc);
    m_drag_size.cx = rc.right-rc.left;
    m_drag_size.cy = rc.bottom-rc.top;

    m_cursor_offset = m_start_cursor_pos;
    m_cursor_offset.x -= rc.left;
    m_cursor_offset.y -= rc.top;

    // デスクトップDCを得る
    desk_hwnd=GetDesktopWindow();
    LockWindowUpdate(desk_hwnd);
    m_desk_hdc= GetDCEx(desk_hwnd, NULL, DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE);
    m_desk_old_brush=(HBRUSH)SelectObject(m_desk_hdc, m_half_tone_brush);

    // リージョン生成
    m_apply_rgn = CreateRectRgn(0, 0, 0, 0);
    m_work_rgn  = CreateRectRgn(0, 0, 0, 0);
    m_work_rgn2 = CreateRectRgn(0, 0, 0, 0);
    m_last_rgn  = CreateRectRgn(0, 0, 0, 0);
    m_target_rgn = CreateRectRgn(0, 0, 0, 0);
}

// ドラッグ中
void DOCK_CONTEXT::drag(RECT target_rc, int target_rc_fill, int flip){
    POINT po;
    RECT rc;
    SIZE size;
    if (!target_rc_fill){
        SetRectRgn(m_work_rgn, target_rc.left, target_rc.top, target_rc.right,target_rc.bottom );
        SetRectRgn(m_work_rgn2, target_rc.left+4, target_rc.top+4, target_rc.right-4, target_rc.bottom-4);
        CombineRgn(m_target_rgn, m_work_rgn, m_work_rgn2, RGN_XOR);
    }
    else SetRectRgn(m_target_rgn, target_rc.left, target_rc.top, target_rc.right, target_rc.bottom);

    if (flip){  // 縦横変換
        size.cx=m_drag_size.cy;
        size.cy=m_drag_size.cx;
    }
    else size=m_drag_size;

    GetCursorPos(&po);
    po.x-=m_cursor_offset.x;
    po.y-=m_cursor_offset.y;
    SetRectRgn(m_work_rgn,  po.x, po.y, po.x+size.cx, po.y+size.cy);
    if (NULLREGION==GetRgnBox(m_target_rgn, &target_rc))
            SetRectRgn(m_work_rgn2, po.x+4, po.y+4, po.x+size.cx-4, po.y+size.cy-4);
    else    SetRectRgn(m_work_rgn2, po.x+1, po.y+1, po.x+size.cx-1, po.y+size.cy-1);
    CombineRgn(m_apply_rgn, m_work_rgn, m_work_rgn2, RGN_XOR);

    CombineRgn(m_work_rgn, m_apply_rgn, m_target_rgn, RGN_OR);
    CombineRgn(m_apply_rgn, m_work_rgn, NULL, RGN_COPY);

    CombineRgn(m_work_rgn, m_apply_rgn, m_last_rgn, RGN_XOR);

    // 反転による矩形描画
    GetRgnBox(m_work_rgn, &rc);
    SelectObject(m_desk_hdc, m_work_rgn);
    PatBlt(m_desk_hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATINVERT);

    // 最後に描画した部分保持(再反転して消去)
    CombineRgn(m_last_rgn, m_apply_rgn, NULL, RGN_COPY);
}
// ドラッグ終了
void DOCK_CONTEXT::end_drag(){
    RECT rc;
    m_now_dragging=0;

    // 最後に描画した部分を消去(再反転してるだけ)
    GetRgnBox(m_apply_rgn, &rc);
    SelectObject(m_desk_hdc, m_apply_rgn);
    PatBlt(m_desk_hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATINVERT);

    // リージョン破棄
    DeleteObject(m_apply_rgn);
    DeleteObject(m_work_rgn);
    DeleteObject(m_work_rgn2);
    DeleteObject(m_last_rgn);
    DeleteObject(m_target_rgn);

    // デスクトップDCを解放
    LockWindowUpdate(NULL);
    if (m_desk_hdc) ReleaseDC(GetDesktopWindow(), m_desk_hdc);
    SelectObject(m_desk_hdc, m_desk_old_brush);
}
//=================== TOOL_BAR ツールバー・クラス ========================
// ツールバー生成
TOOL_BAR::create(DOCK_SYS *p_dock_sys, TBBUTTON *tb_buttons, int tb_button_len){
    m_floating_flag=0;
    m_parent_dock_id =-1;
    m_def_parent_dock_id = m_def_dock_row_num = m_def_dock_offset = -1;
    m_float_pos.x = m_float_pos.y = 200;
    m_p_dock_sys=p_dock_sys;
    m_p_size=&m_horz_size;
    HINSTANCE hInstance=GetModuleHandle(NULL);
    InitCommonControls();
    // コモンコントロールのツールバー生成
    m_control_tool_bar = CreateToolbarEx(p_dock_sys->m_main_frame, WS_CHILD | TBSTYLE_AUTOSIZE | TBSTYLE_WRAPABLE, 0, 6, 
        (HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tb_buttons, tb_button_len, 0, 0, 0, 0, sizeof (TBBUTTON)
    );
    // ツールバーのフレーム生成
    m_hwnd= CreateWindow(
        TEXT("tool_bar_frame"), NULL, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
        0, 0, 0, 0, p_dock_sys->m_main_frame, NULL, hInstance, (LPVOID)this
    );
    renew_size_var();   // サイズ変数の更新
    renew_size();       // 実際のサイズ更新
    SetParent(m_control_tool_bar, m_hwnd);
    ShowWindow(m_control_tool_bar, SW_SHOWNA);
    ShowWindow(m_hwnd, SW_SHOWNA);
}
// ポップアップウィンドウ・プロシージャ(static版)
LRESULT CALLBACK TOOL_BAR::StaticPopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    TOOL_BAR *p_tool_bar =(TOOL_BAR*)GetWindowLong(hwnd, GWL_USERDATA);
    if (!p_tool_bar && (msg==WM_NCCREATE || msg==WM_CREATE)){
        p_tool_bar = (TOOL_BAR*)((LPCREATESTRUCT)lp)->lpCreateParams;
        SetWindowLong(hwnd, GWL_USERDATA, (LONG)p_tool_bar);
    }
    if (p_tool_bar) return p_tool_bar->PopupProc(hwnd, msg, wp, lp);
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ポップアップウィンドウ・プロシージャ
TOOL_BAR::PopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    int res, row_num, offset, insert;
    RECT rc;
    switch (msg) {
    case WM_WINDOWPOSCHANGED:
        GetWindowRect(hwnd, &rc);
        m_float_pos.x=rc.left;
        m_float_pos.y=rc.top;
        break;
    case WM_LBUTTONDBLCLK:
        if (m_def_parent_dock_id<0 || m_def_dock_row_num<0 || m_def_dock_offset<0){
                m_p_dock_sys->do_dock(this, DOCK_BAR::ID_TOP);
        }
        else {
            // ユーザ・デフォルト位置から ドッキング実行
            row_num=m_def_dock_row_num; // get_dock_offset() は行変数を変更するので変数保持
            offset=m_p_dock_sys->m_dock_bar[m_def_parent_dock_id].get_dock_offset(m_def_dock_offset, this, &row_num, &insert);
            m_p_dock_sys->do_dock(this, m_def_parent_dock_id, row_num, offset, insert);
        }
        break;
    case WM_CLOSE:
        if (GetParent(m_hwnd)==hwnd){
            SetParent(m_hwnd, m_p_dock_sys->m_main_frame);
            ShowWindow(m_hwnd, SW_HIDE);
        }
        break;
    case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
        m_p_dock_sys->on_ncactivate(hwnd, wp);
        break;
    case WM_KEYDOWN:    m_p_dock_sys->tool_on_keydown(wp, lp);      break;  // キー押下
    case WM_KEYUP:      m_p_dock_sys->tool_on_keyup(wp, lp);        break;  // キー押上
    case WM_MOUSEMOVE:  m_p_dock_sys->tool_on_mousemove();          break;  // マウス移動(ドラッグ中)
    case WM_LBUTTONDOWN:m_p_dock_sys->tool_on_lbuttondown(this);    break;  // ドラッグ開始
    case WM_LBUTTONUP:  
        KillTimer(m_hwnd, WM_LBUTTONDOWN);
        m_p_dock_sys->tool_on_lbuttonup();      break;  // ドラッグ終了
    case WM_NCHITTEST:
        res=DefWindowProc(hwnd, msg, wp, lp);
        if (res==HTCAPTION) return HTCLIENT;
        return res;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバーのフレームウィンドウ・プロシージャ(static版)
LRESULT CALLBACK TOOL_BAR::StaticToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp){
    TOOL_BAR *p_tool_bar =(TOOL_BAR*)GetWindowLong(hwnd, GWL_USERDATA);
    if (!p_tool_bar && (msg==WM_NCCREATE || msg==WM_CREATE)){
        p_tool_bar = (TOOL_BAR*)((LPCREATESTRUCT)lp)->lpCreateParams;
        SetWindowLong(hwnd, GWL_USERDATA, (LONG)p_tool_bar);
    }
    if (p_tool_bar) return p_tool_bar->ToolBarFrameProc(hwnd, msg, wp, lp);
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバーのフレームウィンドウ・プロシージャ
TOOL_BAR::ToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    RECT rc, rc2;
    HDC hdc;
    PAINTSTRUCT ps;
    switch (msg) {
    case WM_COMMAND:
        SendMessage(m_p_dock_sys->m_main_frame, msg, wp, lp);
        break;
    case WM_LBUTTONDBLCLK:
        m_p_dock_sys->do_float(this, m_float_pos.x, m_float_pos.y, 0);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd , &ps);
        GetClientRect(hwnd, &rc);

        FillRect(hdc, 0, 0, rc.right, 1,    GetSysColorBrush(COLOR_BTNSHADOW));
        FillRect(hdc, 0, 1, rc.right, 2,    GetSysColorBrush(COLOR_BTNHILIGHT));

        if (!m_floating_flag){
            // グリッパー描画
            GetClientRect(hwnd, &rc);
            if (m_vh_type=='H') { rc.left+=2; rc.right=rc.left+3; rc.top+=1; rc.bottom-=2; }
            else                { rc.left+=1; rc.right=rc.left+2; rc.top+=1; rc.bottom-=2; }

            FillRect(hdc, rc.left, rc.top, rc.right-1, rc.top+1,    GetSysColorBrush(COLOR_BTNHILIGHT));
            FillRect(hdc, rc.left, rc.top, rc.left+1,  rc.bottom-1, GetSysColorBrush(COLOR_BTNHILIGHT));

            FillRect(hdc, rc.right, rc.top,   rc.right-1, rc.bottom, GetSysColorBrush(COLOR_BTNSHADOW));
            FillRect(hdc, rc.left, rc.bottom, rc.right, rc.bottom-1, GetSysColorBrush(COLOR_BTNSHADOW));
        }
        EndPaint(hwnd , &ps);
        break;
    case WM_MOUSEMOVE:  m_p_dock_sys->tool_on_mousemove();          break;  // マウス移動(ドラッグ中)
    case WM_LBUTTONDOWN:m_p_dock_sys->tool_on_lbuttondown(this);    break;  // マウス左ボタン押下(ドラッグ開始)
    case WM_LBUTTONUP:  
        KillTimer(m_hwnd, WM_LBUTTONDOWN);
        m_p_dock_sys->tool_on_lbuttonup();      break;  // マウス左ボタン押上(ドラッグ終了)
    case WM_TIMER:
        KillTimer(hwnd, wp);
        switch(wp) {
        case WM_LBUTTONDOWN:
            if (GetKeyState(VK_LBUTTON)<0) {
                m_p_dock_sys->tool_start_drag();
            }
            break;
        }
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバー・縦型に変更
void TOOL_BAR::set_vert(){
    if (!m_control_tool_bar) return;
    m_vh_type='V';
    int style=GetWindowLong(m_control_tool_bar, GWL_STYLE);
    SetWindowLong(m_control_tool_bar, GWL_STYLE, style | CCS_VERT | CCS_NODIVIDER);

    int i, len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
    TBBUTTONINFO tbbi;
    memset(&tbbi, 0, sizeof(TBBUTTONINFO));
    tbbi.cbSize=sizeof(TBBUTTONINFO);
    tbbi.dwMask=TBIF_STATE ;
    for(i=0;i<len;i++){
        SendMessage(m_control_tool_bar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);
        tbbi.fsState |=TBSTATE_WRAP;
        SendMessage(m_control_tool_bar, TB_SETBUTTONINFO, i, (LPARAM)&tbbi);
    }
    m_p_size = &m_vert_size;
}
// ツールバー・横型に変更
void TOOL_BAR::set_horz(){
    if (!m_control_tool_bar) return;
    m_vh_type='H';
    int style=GetWindowLong(m_control_tool_bar, GWL_STYLE);
    SetWindowLong(m_control_tool_bar, GWL_STYLE, style & (~CCS_NODIVIDER) & (~CCS_VERT));

    int i, len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
    TBBUTTONINFO tbbi={0};
    memset(&tbbi, 0, sizeof(TBBUTTONINFO));
    tbbi.cbSize=sizeof(TBBUTTONINFO);
    tbbi.dwMask=TBIF_STATE;
    for(i=0;i<len;i++){
        SendMessage(m_control_tool_bar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);
        tbbi.fsState &= (~TBSTATE_WRAP);
        SendMessage(m_control_tool_bar, TB_SETBUTTONINFO, i, (LPARAM)&tbbi);
    }
    m_p_size = &m_horz_size;
}
// ツールバー・サイズ変数更新
void TOOL_BAR::renew_size_var(){
    RECT rc;
    int i, len;
    DWORD padding;
    TBBUTTONINFO tbbi;
    memset(&tbbi, 0, sizeof(TBBUTTONINFO));
    tbbi.cbSize=sizeof(TBBUTTONINFO);
    tbbi.dwMask=TBIF_STYLE ;

    // 各ボタンから全体サイズ取得
    len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
    m_horz_size.cx=m_horz_size.cy=0;
    m_vert_size.cx=m_vert_size.cy=0;

    if (len>0){
        i=SendMessage(m_control_tool_bar, TB_GETBUTTONSIZE, 0, 0);
        m_horz_size.cy=HIWORD(i);
        m_vert_size.cx=LOWORD(i);
    }

    // ヨコ型のときのサイズ変数
    set_horz();
    for(i=0;i<len;i++){
        if (!SendMessage(m_control_tool_bar, TB_GETITEMRECT, i, (LPARAM) (&rc))) continue;
        m_horz_size.cx+=rc.right-rc.left;
    }
    // パディング(ツールバーのボタン周りの余白部分)取得
    padding=SendMessage(m_control_tool_bar, TB_GETPADDING, 0, 0);
    m_horz_size.cx+=LOWORD(padding);
    m_horz_size.cy+=HIWORD(padding);

    // タテ型のときのサイズ変数
    set_vert();
    for(i=0;i<len;i++){
        if (!SendMessage(m_control_tool_bar, TB_GETITEMRECT, i, (LPARAM) (&rc))) continue;
        m_vert_size.cy+=rc.bottom-rc.top;
    }
    // パディング(ツールバーのボタン周りの余白部分)取得
    padding=SendMessage(m_control_tool_bar, TB_GETPADDING, 0, 0);
    m_vert_size.cx+=LOWORD(padding);
    m_vert_size.cy+=HIWORD(padding);

    if (m_vh_type=='H') set_horz();
    else set_vert();
}
// ツールバー・サイズ更新
void TOOL_BAR::renew_size(){
    int x_offset, y_offset;
    if (!IsWindow(m_control_tool_bar)) return ;

    m_thick=(m_vh_type=='H')?m_horz_size.cy:m_vert_size.cx;
    m_length=(m_vh_type=='H')?m_horz_size.cx:m_vert_size.cy;

    // グリッパーサイズ計算
    x_offset=y_offset=0;
    if (!m_floating_flag){
        m_length+=10;
        if (m_vh_type=='H') x_offset=10;
        else { y_offset=10; x_offset=4; }
    }

    SetWindowPos(m_hwnd, NULL, 0,0, x_offset+m_p_size->cx, y_offset+m_p_size->cy, SWP_NOMOVE);

    // コモンコントロールのサイズ変更は親フレームウィンドウのあとで行う。(サイズ補正されるから)
    // コモンコントロールのサイズ変更と位置移動のSetWindowPosは別に呼ぶ(位置補正されるから)
    SetWindowPos(m_control_tool_bar, NULL, 0, 0, m_p_size->cx, m_p_size->cy, SWP_NOMOVE);
    SetWindowPos(m_control_tool_bar, NULL, x_offset, y_offset, 0, 0, SWP_NOSIZE);
    return;
}
//=================== DOCK_BAR ドックバー・クラス ========================
// ドックバー生成
void DOCK_BAR::create(HWND main_hwnd, int pos_id){
    HINSTANCE hInstance=GetModuleHandle(NULL);
    m_pos_id = pos_id;
    if (pos_id==ID_TOP || pos_id==ID_BOTTOM) m_vh_type='H';
    else m_vh_type='V';
    m_thick=0;
    m_hwnd = CreateWindow(
        TEXT("dock_bar_frame"), NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
        0, 0, 200, 100, main_hwnd, NULL, hInstance, (LPVOID)this
    );
}
// ドックバー・プロシージャ(static版)
LRESULT CALLBACK DOCK_BAR::StaticDockProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    DOCK_BAR *pdock =(DOCK_BAR*)GetWindowLong(hwnd, GWL_USERDATA);
    if (!pdock && (msg==WM_NCCREATE || msg==WM_CREATE)){
        pdock = (DOCK_BAR*)((LPCREATESTRUCT)lp)->lpCreateParams;
        SetWindowLong(hwnd, GWL_USERDATA, (LONG)pdock);
    }
    if (pdock) return pdock->DockBarProc(hwnd, msg, wp, lp);
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ドックバー・プロシージャ
DOCK_BAR::DockBarProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    RECT rc;
    int ri, co;
    HDC hdc;
    PAINTSTRUCT ps;
    switch (msg) {
    case WM_PAINT:
        hdc = BeginPaint(hwnd , &ps);
        GetClientRect(hwnd, &rc);

        FillRect(hdc, 0, 0, rc.right, 1, GetSysColorBrush(COLOR_BTNSHADOW));
        FillRect(hdc, 0, 1, rc.right, 2, GetSysColorBrush(COLOR_BTNHILIGHT));
        co=0;
        for(ri=0;ri<m_row_list.size();ri++){
            co+=m_row_list[ri]->m_thick;
            if (m_vh_type=='H'){
                FillRect(hdc, 0, co,   rc.right, co+1, GetSysColorBrush(COLOR_BTNSHADOW));
                FillRect(hdc, 0, co+1, rc.right, co+2, GetSysColorBrush(COLOR_BTNHILIGHT));
            }
            else {
                FillRect(hdc, co,   0, co+1, rc.bottom, GetSysColorBrush(COLOR_BTNSHADOW));
                FillRect(hdc, co+1, 0, co+2, rc.bottom, GetSysColorBrush(COLOR_BTNHILIGHT));
            }
        }
        EndPaint(hwnd , &ps);
        break;
    case WM_WINDOWPOSCHANGED:
        GetWindowRect(hwnd, &rc);
        if (m_vh_type=='H') { m_front_co = rc.left; m_roof_co = rc.top; }
        else                { m_front_co = rc.top;  m_roof_co = rc.left; }
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバーを放離する
int DOCK_BAR::remove_tool_bar(TOOL_BAR *ptool){
    int ri, ti, tlen;
    DOCK_BAR_ROW *prow;
    for(ri=0;ri<m_row_list.size();ri++) {
        prow=m_row_list[ri];
        tlen=prow->m_ptool_list.size();
        for(ti=0;ti<tlen;ti++){
            if (prow->m_ptool_list[ti]!=ptool) continue;

            // 放離すべきツールバーがあったとき
            prow->m_ptool_list.pop(ti);
            prow->m_tool_offset_list.pop(ti);
            return ri;
        }
    }
    return -1;
}
// ツールバーを合加する
void DOCK_BAR::add_tool_bar(TOOL_BAR *ptool, int row_num, int offset, int insert){
    if (insert || !m_row_list.size()) m_row_list.a_push(row_num);
    DOCK_BAR_ROW *prow=m_row_list[row_num];
    if (!prow) prow=m_row_list.last();
    prow->m_ptool_list.push(ptool);
    prow->m_tool_offset_list.push(offset);
    prow->m_thick=max(prow->m_thick, ptool->m_thick);
}
// ドラッグによるドッキング位置取得
void DOCK_BAR::get_dock_pos_by_drag(RECT *p_drag_rc, TOOL_BAR *ptool, int *p_row_num, int *p_offset, int *p_insert){
    *p_row_num=get_dock_row_num_by_drag(p_drag_rc, p_insert);
    int drag_front, tool_length;
    drag_front = (m_vh_type=='H')? p_drag_rc->left:p_drag_rc->top;
    tool_length = (m_vh_type=='H')? ptool->m_horz_size.cx+10:ptool->m_vert_size.cy+10;

    // ドラッグによるドッキング・オフセット値取得
    *p_offset=drag_front-m_front_co;
    if ((*p_insert)==0) *p_offset=get_dock_offset(*p_offset, ptool, p_row_num, p_insert);

    // 位置補正
    if ((drag_front+tool_length)>(m_front_co+m_length)) *p_offset=m_length-tool_length;
    if (*p_offset <0) *p_offset=0;
}
// aim_frontを基点にドッキング・オフセット値取得
int DOCK_BAR::get_dock_offset(int aim_front, TOOL_BAR *ptool, int *p_row_num, int *p_insert){
    DOCK_BAR_ROW *prow=m_row_list[*p_row_num];
    int ti, tlen, sc, ec, margin_sc, margin_ec, aim_rear, aim_co;
    int tool_length = (m_vh_type=='H')? ptool->m_horz_size.cx+10:ptool->m_vert_size.cy+10;
    if (!prow){
        *p_row_num=m_row_list.size();
        *p_insert=1;
        return aim_front;
    }
    *p_insert = 0;
    aim_rear = aim_front + tool_length;
    aim_co = aim_front + tool_length/2;
    sc=0;
    tlen=prow->m_ptool_list.size();
    for(ti=0;ti<(tlen+1);ti++){
        if (ti<tlen) ec=prow->m_tool_offset_list[ti]+prow->m_ptool_list[ti]->m_length/2;
        else ec=m_length;

        if ((sc<=aim_co && ec>=aim_co) || ti==tlen || (aim_co<0 && ti==0)){
            margin_sc = (ti>=1)? prow->m_tool_offset_list[ti-1]+prow->m_ptool_list[ti-1]->m_length : 0;
            margin_ec = (ti<tlen)? prow->m_tool_offset_list[ti] : m_length;
            if (aim_front>=margin_sc && aim_rear<=margin_ec) return aim_front;  // 無補正で目標位置に挿入可能
            else if (tool_length<=(margin_ec-margin_sc)){   // 挿入位置を補正すれば可能
                if (margin_sc>aim_front) return margin_sc;
                else return margin_ec-tool_length;
            }
            break;  // この行には挿入不可能
        }
        sc=ec;
    }
    *p_insert=1;
    (*p_row_num)++;
    return aim_front;
}
// ドラッグによるドッキング行取得
int DOCK_BAR::get_dock_row_num_by_drag(RECT *p_drag_rc, int *p_insert){
    int ri, rlen=m_row_list.size();
    int sc, ec, drag_co;
    sc=ec=m_roof_co;
    drag_co = (m_vh_type=='H')? p_drag_rc->top+(p_drag_rc->bottom-p_drag_rc->top)/2:
                                p_drag_rc->left+(p_drag_rc->right-p_drag_rc->left)/2;
    *p_insert=0;
    for(ri=0;ri<rlen;ri++){
        ec+=m_row_list[ri]->m_thick;
        if (sc<=drag_co && drag_co<=ec) {
            if (drag_co<(sc+m_row_list[ri]->m_thick/5)) { *p_insert=1; }
            if (drag_co>(ec-m_row_list[ri]->m_thick/5)) { *p_insert=1; ri++; }
            return ri;
        }
    }
    *p_insert=1;
    if (drag_co<m_roof_co) return 0;
    return rlen;
}
// ツールバーがあれば行の"厚さ変数"を更新、無ければ行を削除
void DOCK_BAR::tidy_row(){
    DOCK_BAR_ROW *prow;
    int ri, ti, tlen;

    for(ri=m_row_list.size()-1;ri>=0;ri--) {
        prow=m_row_list[ri];
        // 行の中にツールバーが無いなら行ごと削除
        if (!prow->m_ptool_list.size()) {
            m_row_list.pop(ri);
            continue;
        }

        // 行の"厚さ変数"を更新する
        tlen=prow->m_ptool_list.size();
        prow->m_thick=0;
        for(ti=0;ti<tlen;ti++) prow->m_thick=max(prow->m_thick, prow->m_ptool_list[ti]->m_thick);
    }
}
// ドックバー全体の"厚さ変数" と内部ツールバー位置を更新する
void DOCK_BAR::renew_inside(){
    int ri, ti, tlen, x, y;
    DOCK_BAR_ROW *prow;

    m_thick=0;
    for(ri=0;ri<m_row_list.size();ri++) {
        prow=m_row_list[ri];
        tlen=prow->m_ptool_list.size();
        for(ti=0;ti<tlen;ti++){
            if (m_vh_type=='H') { x=prow->m_tool_offset_list[ti]; y=m_thick; }
            else                { y=prow->m_tool_offset_list[ti]; x=m_thick; }
            SetWindowPos(prow->m_ptool_list[ti]->m_hwnd, NULL, x, y, 0, 0, SWP_NOSIZE);
        }
        m_thick+=prow->m_thick;
    }
}
// 引数 end_line を基準線として tool_num 番目までのツールバーを「左」に寄せる
void DOCK_BAR::shift_left_tool_bar(int row_num, int tool_num, int end_line){
    DOCK_BAR_ROW *prow=m_row_list[row_num];
    if (!prow) return;
    int ti, tmp_pos = end_line;
    TOOL_BAR *ptool;
    for(ti=tool_num;ti>=0;ti--){
        ptool=prow->m_ptool_list[ti];
        if ((prow->m_tool_offset_list[ti]+prow->m_ptool_list[ti]->m_length)<=tmp_pos) break;
        tmp_pos -= ptool->m_length;
        prow->m_tool_offset_list[ti]=tmp_pos;
    }
}
// 引数 end_line を基準線として tool_num 番目以降のツールバーを「右」に寄せる
void DOCK_BAR::shift_right_tool_bar(int row_num, int tool_num, int end_line){
    DOCK_BAR_ROW *prow=m_row_list[row_num];
    if (!prow) return;
    int ti, tmp_pos = end_line;
    TOOL_BAR *ptool;
    for(ti=tool_num;ti<prow->m_ptool_list.size();ti++){
        if (prow->m_tool_offset_list[ti]>=tmp_pos) break;
        ptool=prow->m_ptool_list[ti];
        prow->m_tool_offset_list[ti]=tmp_pos;
        tmp_pos += ptool->m_length;
    }
}
// row_num 番目 の行の全体の余白サイズを取得
int DOCK_BAR::get_total_space(int row_num){
    int ti, total_tool_length=0;
    DOCK_BAR_ROW *prow=m_row_list[row_num];
    if (!prow) return 0;
    for(ti=0;ti<prow->m_ptool_list.size();ti++) total_tool_length += prow->m_ptool_list[ti]->m_length;
    return m_length - total_tool_length;
}
// ドックバー長さ更新
void DOCK_BAR::set_length(int length){
    if (m_length==length) return;
    int ri, ti, tmp_pos, total_tool_length, tool_num, target_row_num;
    DOCK_BAR_ROW *prow;
    TOOL_BAR *ptool;
    m_length=length;
    for(ri=m_row_list.size()-1;ri>=0;ri--){
        total_tool_length=get_total_space(ri);
        prow=m_row_list[ri];

        // 行の中にツールバーが一個しかないか、余白が十分あってツールバー位置補正で間に合う時
        if (prow->m_ptool_list.size()==1 || total_tool_length>=0){
            shift_left_tool_bar(ri, prow->m_ptool_list.size()-1, m_length);
            if (prow->m_ptool_list.size()==1 && prow->m_tool_offset_list[0]<0) prow->m_tool_offset_list[0]=0;
        }
        // 行の中にツールバーが2個以上あって、ツールバー位置補正しても余白が足らない時
        else{
            // 何番目のツールバーからはみ出てるか tool_num に取得
            tool_num=1;
            for(ti=prow->m_ptool_list.size()-1;ti>0;ti--) {
                total_tool_length -= prow->m_ptool_list[ti]->m_length;
                if (total_tool_length<=m_length) { tool_num=ti; break; }
            }

            // ツールバーの行間移動
            target_row_num=ri+1;
            for(ti=tool_num;ti<prow->m_ptool_list.size();ti++) {
                ptool=prow->m_ptool_list[ti];

                // はみ出てるツールバーを target_row_num 番目の行にいれ ポインタを prow に取得
                while(target_row_num<m_row_list.size()) {
                    if (get_total_space(target_row_num)>=ptool->m_length) break;
                    target_row_num++;
                }
                prow = (target_row_num>=m_row_list.size())? m_row_list.a_push() : m_row_list[target_row_num];

                // はみ出てたツールバーを target_row_num 行 に入れて全体ツールバー位置補正
                shift_left_tool_bar(target_row_num, prow->m_ptool_list.size()-1, m_length - ptool->m_length);
                tmp_pos = (m_length>ptool->m_length)? m_length - ptool->m_length : 0;
                add_tool_bar(ptool, target_row_num, tmp_pos, 0);
            }
            // ツールバーの元の行のremove
            prow=m_row_list[ri];
            for(ti=tool_num;ti<prow->m_ptool_list.size();ti++) {
                prow->m_ptool_list.pop(ti);
                prow->m_tool_offset_list.pop(ti);
            }
            // 元の行のツールバー全体の位置補正
            shift_left_tool_bar(ri, prow->m_ptool_list.size()-1, m_length);

        }
    }
    tidy_row(); // 厚さ更新
    renew_inside(); // ツールバー位置補正
}


//=================== DOCK_SYS ドッキングシステム・クラス ========================
// ドッキングシステム生成
DOCK_SYS::create(HWND main_hwnd, HWND child_view_hwnd){
    m_now_nc_activing=0;
    m_main_frame=main_hwnd;
    m_hview=child_view_hwnd;

    regist_winc(TEXT("popup_frame"),    (HBRUSH)(COLOR_BTNFACE+1), TOOL_BAR::StaticPopupProc);      // フローティング・フレーム
    regist_winc(TEXT("tool_bar_frame"), (HBRUSH)(COLOR_BTNFACE+1), TOOL_BAR::StaticToolBarFrameProc); // ツールバーの外枠
    regist_winc(TEXT("dock_bar_frame"), (HBRUSH)(COLOR_BTNFACE+1), DOCK_BAR::StaticDockProc);       // ドックバー

    // ドックバー生成
    m_dock_bar[DOCK_BAR::ID_TOP].create(main_hwnd, DOCK_BAR::ID_TOP);
    m_dock_bar[DOCK_BAR::ID_LEFT].create(main_hwnd, DOCK_BAR::ID_LEFT);
    m_dock_bar[DOCK_BAR::ID_BOTTOM].create(main_hwnd, DOCK_BAR::ID_BOTTOM);
    m_dock_bar[DOCK_BAR::ID_RIGHT].create(main_hwnd, DOCK_BAR::ID_RIGHT);

    // ドックバー優先順位
    m_prio_dock_bar[0] = &m_dock_bar[DOCK_BAR::ID_TOP];
    m_prio_dock_bar[1] = &m_dock_bar[DOCK_BAR::ID_BOTTOM];
    m_prio_dock_bar[2] = &m_dock_bar[DOCK_BAR::ID_LEFT];
    m_prio_dock_bar[3] = &m_dock_bar[DOCK_BAR::ID_RIGHT];
}
// ツールバー生成
void DOCK_SYS::create_tool_bar(TBBUTTON *tb_buttons, int tb_button_len){
    TOOL_BAR *ptool;
    ptool = new TOOL_BAR;
    m_ptool_list.push(ptool);
    ptool->create(this, tb_buttons, tb_button_len);
    do_dock(ptool, DOCK_BAR::ID_TOP);
}

// タイトルバーのアクティブ化・非アクティブ化
void DOCK_SYS::on_ncactivate(HWND hwnd, WPARAM wp){
    if (m_now_nc_activing) return;
    m_now_nc_activing=1;
    SendMessage(m_main_frame, WM_NCACTIVATE, wp, 0);

    HWND tmp_hwnd;
    for(int i=0;i<m_ptool_list.size();i++){
        tmp_hwnd=m_ptool_list[i]->m_popup_frame;
        if (!IsWindow(tmp_hwnd)) continue;
        SendMessage(tmp_hwnd, WM_NCACTIVATE, wp, 0);
    }
    m_now_nc_activing=0;
}

// ツールバー・キー押下
void DOCK_SYS::tool_on_keydown(WPARAM wp, LPARAM lp){
    if (!m_dock_context.m_now_dragging) {
        // 既にマウス左ボタンは押下されている
        if (m_dock_context.m_pre_started) tool_start_drag();
        else return;
    }
    tool_drag();    // ツールバー・ドラッグ
}
// ツールバー・キー押上
void DOCK_SYS::tool_on_keyup(WPARAM wp, LPARAM lp){
    tool_drag();    // ツールバー・ドラッグ
}
// マウス移動
void DOCK_SYS::tool_on_mousemove(){
    POINT po;
    if (!m_dock_context.m_now_dragging) {
        // 既にマウス左ボタンは押下されている
        if (m_dock_context.m_pre_started){
            GetCursorPos(&po);
            po.x -= m_dock_context.m_start_cursor_pos.x;
            po.y -= m_dock_context.m_start_cursor_pos.y;
            // ダブルクリックの許容範囲を越えて移動したらタイマを待たずにドラッグ開始
            if (m_dock_context.m_doubleclk_size.cx<abs(po.x) || m_dock_context.m_doubleclk_size.cy<abs(po.y)){
                KillTimer(m_dock_context.m_p_tool_bar->m_hwnd, WM_LBUTTONDOWN);
                tool_start_drag();
            }
        }
        else return;
    }
    tool_drag();    // ツールバー・ドラッグ
}
// マウス左ボタン押下
void DOCK_SYS::tool_on_lbuttondown(TOOL_BAR *ptool){
    m_dock_context.m_pre_started=1;
    m_dock_context.pre_start_drag(ptool);
    SetTimer(ptool->m_hwnd, WM_LBUTTONDOWN, GetDoubleClickTime()+5, NULL);
}
// マウス左ボタン押上
void DOCK_SYS::tool_on_lbuttonup(){
    if (m_dock_context.m_pre_started) ReleaseCapture();
    m_dock_context.m_pre_started=0;

    if (!m_dock_context.m_now_dragging) return;
    TOOL_BAR *ptool=m_dock_context.m_p_tool_bar;
    m_dock_context.end_drag();  // ドラッグ終了

    RECT drag_rc;
    int dock_id, flip, row_num, offset, insert;
    m_dock_context.get_drag_rect(&drag_rc); // ドラッグ矩形の取得
    dock_id = get_dock_by_drag(&drag_rc, ptool->m_parent_dock_id, &flip); // ドラッグの時のドッキング先取得

    if (dock_id>=0) {
        // ドラッグによるドッキング位置取得
        m_dock_bar[dock_id].get_dock_pos_by_drag(&drag_rc, ptool, &row_num, &offset, &insert);
        // ツールバーのダブルクリックした時ドッキング先の変数
        ptool->m_def_parent_dock_id = dock_id;
        ptool->m_def_dock_row_num   = row_num;
        ptool->m_def_dock_offset    = offset;
        // ドッキング実行
        do_dock(ptool, dock_id, row_num, offset, insert);
    }
    // フローティング実行
    else do_float(ptool, drag_rc.left, drag_rc.top, flip);
}
// ドラッグの時のドッキング先取得
int DOCK_SYS::get_dock_by_drag(RECT *p_drag_rc, int now_dock_id, int *p_flip){
    RECT dock_rc;
    *p_flip = (GetKeyState(VK_SHIFT)<0)? 1 : 0;
    int forced_float = (*p_flip || (GetKeyState(VK_CONTROL)<0))? 1: 0;  // 強制フロート

    // ドッキング・フローティング判定
    DOCK_BAR *pdock;
    if (!forced_float){
        // ドッキング判定
        for(int i=-1;i<4;i++){
            if (i<0) pdock=&m_dock_bar[now_dock_id];
            else pdock=m_prio_dock_bar[i];
            GetWindowRect(pdock->m_hwnd, &dock_rc);
            if (p_drag_rc->left<=dock_rc.right && p_drag_rc->right>=dock_rc.left
                    && p_drag_rc->top<=dock_rc.bottom && p_drag_rc->bottom>=dock_rc.top){
                return pdock->m_pos_id;
            }
        }
    }
    // フローティング判定
    return -1;
}
// ツールバー・ドラッグ・スタート
void DOCK_SYS::tool_start_drag(){
    if (m_dock_context.m_now_dragging) return;
    TOOL_BAR *ptool=m_dock_context.m_p_tool_bar;
    m_dock_context.start_drag();    // ドラッグ開始

    // ツールバーを旧親ドックバーから放離させる
    if (ptool->m_parent_dock_id>=0) m_dock_bar[ptool->m_parent_dock_id].remove_tool_bar(ptool);
}
// ツールバー・ドラッグ
void DOCK_SYS::tool_drag(){
    if (!m_dock_context.m_now_dragging) return;
    int filp = (GetKeyState(VK_SHIFT)<0)? 1 : 0;
    int ri, head, dock_id, flip;
    RECT drag_rc, rc;
    TOOL_BAR *ptool=m_dock_context.m_p_tool_bar;
    m_dock_context.get_drag_rect(&drag_rc); // ドラッグ矩形の取得
    dock_id = get_dock_by_drag(&drag_rc, m_dock_context.m_p_tool_bar->m_parent_dock_id, &flip); // ドラッグの時のドッキング先取得

    DOCK_BAR *pdock;
    int row_num, offset, insert;
    SIZE size;

    // ドッキング先描画
    if (dock_id>=0){
        pdock=&m_dock_bar[dock_id];
        pdock->get_dock_pos_by_drag(&drag_rc, ptool, &row_num, &offset, &insert);
        GetWindowRect(pdock->m_hwnd, &rc);
        head=0;
        for(ri=0;ri<row_num;ri++) head+=pdock->m_row_list[ri]->m_thick;
        if (pdock->m_vh_type=='H')  { rc.left += offset; rc.top  += head; size=ptool->m_horz_size; size.cx+=10; }
        else                        { rc.top += offset;  rc.left += head; size=ptool->m_vert_size; size.cy+=10; }

        rc.right  = rc.left + size.cx;
        rc.bottom = rc.top  + size.cy;
        if (insert){
            if (dock_id==DOCK_BAR::ID_TOP)  rc.bottom = rc.top + 4;
            if (dock_id==DOCK_BAR::ID_LEFT) rc.right  = rc.left + 4;
            if (dock_id==DOCK_BAR::ID_BOTTOM) rc.bottom = rc.top - 4;
            if (dock_id==DOCK_BAR::ID_RIGHT)  rc.right  = rc.left - 4;
        }
    }
    else rc.left=rc.top=rc.right=rc.bottom=0;

    m_dock_context.drag(rc, insert, filp);  // ドラッグ中
};

// ドッキング実行(ドッキング位置自動)
void DOCK_SYS::do_dock(TOOL_BAR *ptool, int dock_id){
    int ri, ti, row_num=0, offset=0, insert, sc, tool_length;
    DOCK_BAR *pdock=&m_dock_bar[dock_id];
    DOCK_BAR_ROW *prow;
    if (ptool->m_parent_dock_id>=0) m_dock_bar[ptool->m_parent_dock_id].remove_tool_bar(ptool);

    tool_length = (ptool->m_floating_flag)? ptool->m_length+10 : ptool->m_length;

    row_num=pdock->m_row_list.size();
    offset=0;
    for(ri=0;ri<pdock->m_row_list.size();ri++){
        prow=pdock->m_row_list[ri];
        if (tool_length<pdock->get_total_space(ri)){
            row_num=ri;
            offset=prow->m_tool_offset_list.last()+prow->m_ptool_list.last()->m_length;
            for(ti=0;ti<(prow->m_ptool_list.size());ti++){
                sc=(ti>0)? prow->m_tool_offset_list[ti-1]+prow->m_ptool_list[ti-1]->m_length : 0;
                if ((prow->m_tool_offset_list[ti]- sc)>=tool_length) { offset = sc; break; }
            }
            break;
        }
    }
    offset=pdock->get_dock_offset(offset, ptool, &row_num, &insert);
    if (insert) offset=0;

    do_dock(ptool, dock_id, row_num, offset, insert);
}

// ドッキング実行
void DOCK_SYS::do_dock(TOOL_BAR *ptool, int dock_id, int row_num, int offset, int insert){
    ptool->m_floating_flag = 0;
    DOCK_BAR *pdock, *p_last_dock=NULL;
    if (ptool->m_parent_dock_id>=0) p_last_dock=&m_dock_bar[ptool->m_parent_dock_id];

    ptool->m_parent_dock_id = dock_id;
    pdock=&m_dock_bar[dock_id];
    if (pdock->m_vh_type=='V') ptool->set_vert();
    else ptool->set_horz();
    SetParent(ptool->m_hwnd, pdock->m_hwnd);    // ツールバーの親ウィンドウ変更
    ShowWindow(ptool->m_hwnd, SW_SHOWNA);       // ツールバーを表示する(ポップアップ時に閉じた時対策)
    ptool->renew_size();    // ツールバー・サイズ更新
    pdock->add_tool_bar(ptool, row_num, offset, insert);    // ドックバーにツールバーを合加する
    if (ptool->m_def_parent_dock_id<0 || ptool->m_def_dock_row_num<0 || ptool->m_def_dock_offset<0){
        ptool->m_def_parent_dock_id = dock_id;
        ptool->m_def_dock_row_num   = row_num;
        ptool->m_def_dock_offset    = offset;
    }

    DestroyWindow(ptool->m_popup_frame);    // ポップアップ・ウィンドウ削除
    if (p_last_dock){
        p_last_dock->tidy_row();    // ツールバーがあれば行の"厚さ変数"を更新、無ければ行を削除
        if (p_last_dock!=pdock) p_last_dock->renew_inside();    // "厚さ変数" と内部ツールバー位置を更新
    }
    pdock->renew_inside();  // "厚さ変数" と内部ツールバー位置を更新する
    recalc_layout();
}

// フローティング
void DOCK_SYS::do_float(TOOL_BAR *ptool, int x, int y, int flip){
    if (flip){
        if (ptool->m_vh_type=='V') ptool->set_horz();
        else ptool->set_vert();
    }
    // ポップアップ・フレーム生成
    if (!IsWindow(ptool->m_popup_frame)) {
        ptool->m_popup_frame= CreateWindowEx(
            WS_EX_TOOLWINDOW, TEXT("popup_frame"), NULL,
            WS_OVERLAPPEDWINDOW | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            0, 0, 0, 0, m_main_frame, NULL, GetModuleHandle(NULL), (LPVOID)ptool
        );
    }
    DOCK_BAR *pdock;
    if (ptool->m_parent_dock_id>=0){
        pdock=&m_dock_bar[ptool->m_parent_dock_id];
        pdock->remove_tool_bar(ptool);
        pdock->tidy_row();  // ツールバーがあれば行の"厚さ変数"を更新、無ければ行を削除
        pdock->renew_inside();  // "厚さ変数" と内部ツールバー位置を更新する
    }
    ptool->m_parent_dock_id = -1;

    SetParent(ptool->m_hwnd, ptool->m_popup_frame);
    ptool->m_floating_flag = 1;
    ptool->renew_size();    // ツールバー・サイズ更新
    // ポップアップ・ウィンドウ位置サイズ
    RECT rc;
    rc.left=rc.top=0;
    rc.right=ptool->m_p_size->cx;
    rc.bottom=ptool->m_p_size->cy;
    AdjustWindowRectEx(&rc, GetWindowLong(ptool->m_popup_frame, GWL_STYLE),
                FALSE, GetWindowLong(ptool->m_popup_frame, GWL_EXSTYLE));
    SetWindowPos(ptool->m_popup_frame, NULL, x, y, rc.right-rc.left, rc.bottom-rc.top, 0);
    SetWindowPos(ptool->m_hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE);

    ShowWindow(ptool->m_popup_frame, SW_SHOWNA);
    ShowWindow(ptool->m_hwnd, SW_SHOWNA);
    recalc_layout();
}
// レイアウト再計算
void DOCK_SYS::recalc_layout(){
    if (IsIconic(m_main_frame)) return;
    RECT rc, main_rc;
    // 子ウィンドウ位置の再計算
    GetClientRect(m_main_frame, &rc);
    main_rc = rc;

    // ドックバー位置サイズ
    DOCK_BAR *pdock;
    for(int i=0;i<4;i++){
        pdock=m_prio_dock_bar[i];
        if (pdock->m_pos_id==DOCK_BAR::ID_TOP)  {
            pdock->set_length(rc.right-rc.left);    // 長さ更新、内部でm_thick が更新されることもある
            rc.top  += pdock->m_thick;
            SetWindowPos(pdock->m_hwnd, NULL, rc.left, 0, rc.right-rc.left, rc.top, 0);
        }
        if (pdock->m_pos_id==DOCK_BAR::ID_LEFT) {
            pdock->set_length(rc.bottom-rc.top);
            rc.left += pdock->m_thick;
            SetWindowPos(pdock->m_hwnd, NULL, 0, rc.top, rc.left, rc.bottom-rc.top, 0);
        }
        if (pdock->m_pos_id==DOCK_BAR::ID_BOTTOM) {
            pdock->set_length(rc.right-rc.left);
            rc.bottom -= pdock->m_thick;
            SetWindowPos(pdock->m_hwnd,NULL, rc.left, rc.bottom, rc.right-rc.left, main_rc.bottom-rc.bottom, 0);
        }
        if (pdock->m_pos_id==DOCK_BAR::ID_RIGHT)  {
            pdock->set_length(rc.bottom-rc.top);
            rc.right  -= pdock->m_thick;
            SetWindowPos(pdock->m_hwnd, NULL, rc.right, rc.top, main_rc.right-rc.right, rc.bottom-rc.top, 0);
        }
    }
    // ビューウィンドウ位置サイズ
    if (IsWindow(m_hview)) {
        SetWindowPos(m_hview, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0);
        InvalidateRect(m_hview, NULL, TRUE);
    }
}
// ウィンドウクラス登録
DOCK_SYS::regist_winc(char *caption, HBRUSH hbr_bg, WNDPROC proc){
    WNDCLASS winc;

    winc.style              = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    winc.lpfnWndProc        = proc;
    winc.cbClsExtra = winc.cbWndExtra       = 0;
    winc.hInstance          = GetModuleHandle(NULL);
    winc.hIcon              = LoadIcon(NULL, IDI_APPLICATION);
    winc.hCursor            = LoadCursor(NULL, IDC_ARROW);
    winc.hbrBackground      = hbr_bg;
    winc.lpszMenuName       = NULL;
    winc.lpszClassName      = caption;

    if (!RegisterClass(&winc)) return -1;
    return 0;
}
// デストラクタ
DOCK_SYS::~DOCK_SYS(){
    int i, len=m_ptool_list.size();
    for(i=0;i<len;i++){
        delete (m_ptool_list[i]);
    }
}
//======================================================================================
//============ 以下  メインウィンドウ ・ ビューウィンドウ ==============================
//======================================================================================

// ビューウィンドウ・プロシージャ
LRESULT CALLBACK ChildViewProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    HDC hdc;
    PAINTSTRUCT ps;
    char str[]="ChildViewウィンドウ(座標0,0)";

    switch (msg) {
    case WM_LBUTTONDOWN:
        SendMessage(GetParent(hwnd), WM_LBUTTONDOWN, wp, lp);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd , &ps);
        TextOut(hdc, 0, 0, str, lstrlen(str));
        EndPaint(hwnd , &ps);
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

// ツールバーのボタン配列
TBBUTTON tool_buttons1[] = {
    { STD_FILENEW, 1, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_FILEOPEN, 2, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_FILESAVE, 3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0 },
    { STD_COPY, 4, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_CUT, 5, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
};
TBBUTTON tool_buttons2[] = {
    { STD_DELETE, 6, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0 },
    { VIEW_NEWFOLDER, 7, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
};
TBBUTTON tool_buttons3[] = {
    { STD_FILESAVE, 3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0 },
    { STD_COPY, 4, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_CUT, 5, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { VIEW_PARENTFOLDER, 8, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
};
// メインウィンドウ・プロシージャ
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    static DOCK_SYS dock_sys;   // ウィンドウ・ハンドルの一括化 構造体
    static HWND hview;
    static n=0;

    switch (msg) {
    case WM_CREATE:
        hview= CreateWindowEx(
            WS_EX_TOOLWINDOW, TEXT("child_view"), NULL, WS_CHILD | WS_VISIBLE,
            0, 0, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, NULL
        );
        dock_sys.create(hwnd, hview);
        dock_sys.create_tool_bar(tool_buttons1, 6);     // ツールバー1を作成
        dock_sys.create_tool_bar(tool_buttons2, 3);     // ツールバー2を作成
        dock_sys.create_tool_bar(tool_buttons3, 5);     // ツールバー3を作成
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
        dock_sys.on_ncactivate(hwnd, wp);
        break;
    case WM_KEYUP:
    case WM_KEYDOWN:
        dock_sys.tool_drag();
        break;
    case WM_SIZE:
        dock_sys.recalc_layout();
        break;
    case WM_LBUTTONDOWN:
        n++;
        if      ((n%6)==0) dock_sys.do_dock(dock_sys.m_ptool_list[0], DOCK_BAR::ID_TOP);
        else if ((n%6)==1) dock_sys.do_dock(dock_sys.m_ptool_list[0], DOCK_BAR::ID_LEFT);
        else if ((n%6)==2) dock_sys.do_dock(dock_sys.m_ptool_list[0], DOCK_BAR::ID_BOTTOM);
        else if ((n%6)==3) dock_sys.do_dock(dock_sys.m_ptool_list[0], DOCK_BAR::ID_RIGHT);
        else if ((n%6)==4) dock_sys.do_float(dock_sys.m_ptool_list[0], 200, 200, 1);
        else if ((n%6)==5) dock_sys.do_float(dock_sys.m_ptool_list[0], 200, 200, 1);
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow ) {
    MSG msg;
    WNDCLASS winc;
    HWND hwnd;

    winc.style              = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc        = WndProc;
    winc.cbClsExtra = winc.cbWndExtra       = 0;
    winc.hInstance          = hInstance;
    winc.hIcon              = LoadIcon(NULL, IDI_APPLICATION);
    winc.hCursor            = LoadCursor(NULL, IDC_ARROW);
    winc.hbrBackground      = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpszMenuName       = NULL;
    winc.lpszClassName      = TEXT("main_frame");

    if (!RegisterClass(&winc)) return -1;   // メイン・ウィンドウ

    winc.hbrBackground      = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpfnWndProc        = ChildViewProc;
    winc.lpszClassName      = TEXT("child_view");
    if (!RegisterClass(&winc)) return -1;   // ビューウィンドウ

    hwnd = CreateWindow(
        TEXT("main_frame"), TEXT("ドッキング・ツールバーのテスト"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
        100, 100, 240, 200, NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL) return -1;

    while(GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
    }

    return msg.wParam;
}




      


 004-05  前へ←  ホームへ  →次へ  004-07