004-04  前へ←  ホームへ  →次へ  004-06

No.004 - 05  複数ドッキング・ツールバー の実装


前回までの自前ドッキング・ツールバーは単独のツールバーしか扱えませんでした。
今回は複数ドッキングツールバーを扱います。

まず複数ドッキングツールバーを扱うための課題を考証します。

・ドックバーが縦と横で重なる部分はどちらが優先?
ツールバーが複数になると複数のドックバーも同時に表示されるようになり、メインウィンドウのクライアント領域の四隅にドックバーが互いに重なる部分ができます。
必然的に縦か横のドックバーの優先度の高いほうのドックバーがその部分を占有し、優先度の低いほうはその部分サイズが小さくなります。
以下のサンプルでは "DOCK_BARポインタ" の配列を用意し、上下左右の DOCK_BARのポインタを 優先順に格納しています。
特殊なサイズ変数(厚さ)を用意しておいて、子ウィンドウのサイズ変更と同時に優先順にDOCK_BARのサイズを一斉に更新します。

・ドッキング位置の判定は?
ドラッグ位置からドッキング位置を計算するのに、既存の行にドッキングする場合と、新規の行を作って挿入する場合の計算式の仕様はどうするといいでしょうか。筆者がかなり試行錯誤した結果、次のような感じにしました。

ツールバー太さの中央の座標をツールバー側の計算基準線として、まずその基準線がどの行に重なっているか得ます。
基準線がどの行にも重なっていないなら先頭もしくは末尾新しく行を追加し、そこにドッキングします。
重なっている行が取得できたなら、その行の中での位置から再び計算します。
ツールバーの基準線が行の中央部分の5分の3の中にあるならその行にドッキングします。
両端5分の1、5分の1にあるならそちら側に新しく行を追加し、そこにドッキングします。

・ツールバーを挿入したい位置に他のツールバーがあったら?
EXCELなどのドッキングツールバーは挿入先に既存ツールバーがあればそれを強制的に移動してドラッグ中のツールバーを割り込ませますが、今回のソースでは既存ツールバーを強制移動させることはしません。
指定位置(もしくは指定位置に近い場所)にに挿入できない場合はドックバー内に新規に行を作成挿入し、そこにドラッグ中のツールバーを配置するようにします。

・BARの概念
ドックバーやツールバーのようなBARは通常のXY座標で考えると大変です。
縦の場合と横の場合でそれぞれにXYを反転して計算する必要があります。
(現にMFCはSetWindowPosでXYを反転させている。かえって面倒なことになってるけど。)
それではソースも長くなるしデバッグも大変なので特殊な変数を使います。

BARをひとつの筒のようにみなし、
サイズに関しては「厚さと長さ(m_thickとm_length)」というサイズ変数を使います。
縦の場合はX幅を厚さ、Y幅を長さ
横の場合はY幅を厚さ、X幅を長さ
になります。
位置は前後(m_front_co, m_rear_co)と屋根(m_roof_co)と床(m_floor_co)という概念で変数を設定します。
(といっても床にあたる変数は今回、ありませんが・・・)
縦の場合はX座標が前後、Y座標を屋根・床
横の場合はY座標が前後、X座標を屋根・床
になります。
ちなみに_co は座標(coordinate)の略です。


複数ツールバーは単独のそれと大きく違います。
可変長リスト構造を使う必要があったため当サイト・ライブラリのXLISTPLISTテンプレートを使っています。

XLIST使い方
XLIST<クラス名> 変数名; ・・・宣言
push(値); ・・・アイテムをリストに追加
pop(n); ・・・n番目のアイテムをリストから削除
変数名[n]; ・・・配列的にアクセス
size(); ・・・現在のアイテム数の取得
PLIST使い方
PLIST<クラス名> 変数名;  ・・・宣言(内部でXLIST<クラス名*>を宣言している)
push(アイテムポインタ); ・・・アイテムへのポインタをリストに追加
a_push(void) ・・・内部でnew でメモリを確保し、そのポインタをリストに追加
pop(n); ・・・n番目のアイテムをリストから削除
変数名[n]; ・・・配列的にアクセス
size(); ・・・現在のアイテム数の取得
XLIST<TOOL_BAR>はTOOL_BARの可変長リスト、
XLIST<TOOL_BAR*>はTOOL_BAR*の可変長リストを生成する。
PLIST<TOOL_BAR>はTOOL_BAR* の可変長リストを生成する。
デフォルトの状態では内部でnew deleteをする。
(以下のソースはデフォルトのまま使用)

ツールバーが複数になったので、ドックバーに行クラスを作ってレバーコントロールのような動作を追加します。
じゃあレバーコントロール使えばいいじゃん・・・と思うかもしれませんが、レバーコントロールも何でもしてくれる訳じゃないのであえて自前クラスで実装です。



クラスのメンバが増えてごちゃごちゃしてきたので、一旦各クラスの全解説します。


・DOCK_BAR_ROW ドックバー行クラス
レバーコントロールのような行を作ります。このクラスにツールバーポインタ等を持たせ管理します。

メンバ変数
XLIST<TOOL_BAR*> m_ptool_list はこの行の中にあるツールバーの各ポインタリストです。左から並ぶようにリストに値を入れます。
XLIST<int> m_tool_offset_list;
int m_thick は行の太さ(==この行の中にある最も太いツールバーの太さ)です。

・メンバ関数
メンバ関数はありません。


・DOCK_BAR ドックバー・クラス

メンバ変数

enum { ID_TOP, ID_LEFT, ID_BOTTOM, ID_RIGHT } は位置ID用の列挙定数です。
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 は厚さ変数です。縦型ならX幅、横型ならY幅の値です。
int m_front_co, m_rear_co, m_roof_co;は特殊位置変数です。(上記BARの概念参照)
PLIST<DOCK_BAR_ROW> m_row_list は行クラスの拡張リストです。

・メンバ関数
プロシージャ
DockBarProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
static LRESULT CALLBACK StaticDockProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)

プロシージャです。
WM_PAINTで各行の区切り線を描画し、WM_WINDOWPOSCHANGED で特殊位置変数を更新しています。

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 drag_front, TOOL_BAR *ptool, int tool_length, int *p_row_num, int *p_insert)

この3関数はドッキング位置を取得します。
get_dock_pos_by_drag()はドックバー内のドラッグ統括的関数で、ドラッグ矩形(スクリーン座標)からドッキング位置(スクリーン座標)を計算します。
get_dock_row_num_by_drag() はドラッグ矩形(スクリーン座標)からドッキング行を取得します。
get_dock_offset() は目的位置(クライアント座標)からドッキング・オフセット位置(クライアント座標)を取得します。

void tidy_row() は行の中にツールバーがあれば"行の厚さ変数"を更新し、無ければ行を削除します。
void renew_inside() は"ドックバーの厚さ変数" と内部ツールバー位置を更新する


・TOOL_BAR ツールバー・クラス

メンバ変数
HWND m_hwnd, m_control_tool_bar, m_popup_frame は各HWNDです。m_hwnd はツールバーのフレームのウィンドウ・ハンドル、m_control_tool_bar はコモンコントロール・ツールバー, m_popup_frame はポップアップ・フレームです。
DOCK_SYS *m_p_dock_sys はドッキング・ツールバー統括クラスへのポインタです。
int m_floating_flag はフローティング・フラグです。 浮いてれば==1 そうでないなら==0。
int m_vh_typeは縦横フラグです。 縦なら=='V' 横なら=='H'です。親ドックバーによって変更します。
int m_parent_dock_id
は親ドックバーの位置IDです。
SIZE *m_p_size, m_horz_size, m_vert_size はサイズ変数です。常に縦サイズ、横サイズを保持しておいてドラッグ時の演算に備えます。m_p_sizeはツールバーが横ならm_horz_sizeへのポインタ, 縦ならm_vert_sizeへのポインタです。m_horz_size,m_vert_sizeにはグリッパーのサイズを入れずに保持します。
int m_thick, m_length;は厚さと長さ変数です。(上記BARの概念参照)

・メンバ関数
create(DOCK_SYS *p_dock_sys, TBBUTTON *tb_buttons, int tb_button_len) は自身であるツールバーを生成します。
set_horz() はツールバーを横型に変更します。
set_vert() はツールバーを縦型に変更します。
void renew_size_var() はサイズ変数m_horz_size,m_vert_sizeの更新をします。セパレータのサイズ取得バグを回避するため、ちょっと変なコーディングになっています。
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);

プロシージャはポップアップもツールバー・フレームもほぼ同様の動作をしています。
ドラッグ操作を検知するとDOCK_SYSクラスに制御を預けます。


・DOCK_CONTEXT ドック・コンテキスト

メンバ変数
HRGN m_apply_rgn, m_work_rgn, m_work_rgn2, m_last_rgn, m_target_rgn は各リージョンです。m_apply_rgnは実際に反転したままにしたい部分、m_target_rgnはドッキング先を表示するリージョンです。
HDC m_desk_hdc; はデスクトップDCです。
int m_now_dragging; ドラッグ中フラグです。 ドラッグ中なら==1 そうでないなら==0。
SIZE m_drag_size はドラッグ中の矩形サイズです。
POINT m_cursor_offset はドラッグ中の矩形位置のマウス位置との差分です。
HBRUSH m_desk_old_brush はデスクトップDC・ブラシです。デスクトップDCのロック解除時にこのブラシに戻します。
HBRUSH m_half_tone_brush はハーフトーン・ブラシです。(004-02 D&D編参照)。
TOOL_BAR *m_p_tool_bar はドラッグ中のツールバーへのポインタです。

・メンバ関数
DOCK_CONTEXT() はコンストラクタです。ハーフトーン・ブラシを生成しています。
~DOCK_CONTEXT() はデストラクタです。ハーフトーン・ブラシを破棄しています。
void start_drag(TOOL_BAR *ptool) はドラッグを開始します。
void drag(RECT target_rc, int target_rc_fill, int flip) はドラッグ中の処理をします。
void end_drag() はドラッグ終了の処理をします。
void get_drag_rect(RECT *p_drag_rc) はドラッグ中の矩形を取得します。


・DOCK_SYS ドッキング・ツールバー統括クラス

メンバ変数
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] はドックバークラスです。上下左右に配置する4つです。
DOCK_BAR *m_prio_dock_bar[4] はドックバー優先順位の高い順にポインタを格納しています。
XLIST<TOOL_BAR*> m_ptool_list は制御するツールバー群のポインタです。


・メンバ関数
~DOCK_SYS() はデストラクタです。生成(new演算)したツールバーを破棄しています。
regist_winc(char *caption, HBRUSH hbr_bg, WNDPROC proc) はウィンドウクラス登録関数です。
create(HWND main_hwnd, HWND child_view_hwnd) 自身DOCK_SYSの生成をします。
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_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) はツールバー生成し、そのポインタをメンバ変数m_tool_listに加えます。
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)もドッキングを実行します。指定行の指定位置にドッキングします。「insertは新しく行を作ってそこにドッキングする」フラグです。insert==1で新規行に、insert==0で既存行にドッキングします。
void do_float(TOOL_BAR *ptool, int x, int y, int flip) はフローティングを実行します。


メインウィンドウ
複数ツールバーになってもメインウィンドウは前回同様Cのままです。

プロシージャで
WM_CREATEメッセージでビューウィンドウと
DOCK_SYS生成後、各種ツールバーを作成するようになっています。

WM_NCACTIVATE メッセージ でdock_sys.on_ncactivate(hwnd, wp);関数を呼びタイトルバーの青の処理
WM_KEYUPWM_KEYDOWNメッセージ でdock_sys.tool_drag();を呼出し、ドラッグ中のコントロール・キー、シフトキーをフックします。
WM_SIZEメッセージでrecalc_layout() 関数を呼出各ドックバー、ビューウィンドウの再配置をしています。



いろんなのできます。


docking_test5.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;
    return 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;

    void 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
    SIZE m_drag_size;       // ドラッグ中の矩形サイズ
    POINT m_cursor_offset;  // ドラッグ中の矩形位置のマウス位置との差分
    HBRUSH m_desk_old_brush, m_half_tone_brush; // デスクトップDC・ブラシ  /  ハーフトーン・ブラシ
    TOOL_BAR *m_p_tool_bar; // ドラッグ中のツールバー
    int m_now_dragging;     // ドラッグ中なら==1  そうでないなら==0

    DOCK_CONTEXT();     // コンストラクタ
    ~DOCK_CONTEXT();    // デストラクタ
    void start_drag(TOOL_BAR *ptool); // ドラッグ開始
    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 tool_length, int *p_row_num, int *p_insert);

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

// ドッキング・ツールバー統括クラス
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_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_now_dragging=0;

    // ハーフトーン・パレット生成
    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::start_drag(TOOL_BAR *ptool){
    HWND desk_hwnd;
    RECT rc;
    m_now_dragging=1;
    m_p_tool_bar = ptool;
    HWND hwnd = (ptool->m_floating_flag)? ptool->m_popup_frame : ptool->m_hwnd;
    SetCapture(hwnd);

    // デスクトップ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);

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

    GetCursorPos(&m_cursor_offset);
    m_cursor_offset.x -= rc.left;
    m_cursor_offset.y -= rc.top;

    // リージョン生成
    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;
    ReleaseCapture();

    // 最後に描画した部分を消去(再反転してるだけ)
    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 ツールバー・クラス ========================
// ツールバー生成
void TOOL_BAR::create(DOCK_SYS *p_dock_sys, TBBUTTON *tb_buttons, int tb_button_len){
    m_floating_flag=0;
    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;
    switch (msg) {
    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_LBUTTONDOWN:m_p_dock_sys->tool_on_lbuttondown(this);    break;  // ドラッグ開始
    case WM_LBUTTONUP:  m_p_dock_sys->tool_on_lbuttonup();          break;  // ドラッグ終了
    case WM_MOUSEMOVE:  m_p_dock_sys->tool_on_mousemove();          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;
    HDC hdc;
    PAINTSTRUCT ps;
    switch (msg) {
    case WM_COMMAND:
        SendMessage(m_p_dock_sys->m_main_frame, msg, wp, lp);
        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_LBUTTONDOWN:m_p_dock_sys->tool_on_lbuttondown(this);break;  // マウス左ボタン押下(ドラッグ終了)
    case WM_LBUTTONUP:  m_p_dock_sys->tool_on_lbuttonup();      break;  // マウス左ボタン押上(ドラッグ開始)
    case WM_MOUSEMOVE:  m_p_dock_sys->tool_on_mousemove();      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();

    int ti, tool_num=prow->m_ptool_list.size();
    for(ti=0;ti<prow->m_ptool_list.size();ti++){
        if (offset<prow->m_tool_offset_list[ti]){
            tool_num=ti;
            break;
        }
    }
    prow->m_ptool_list.push(ptool, tool_num);
    prow->m_tool_offset_list.push(offset, tool_num);
    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, tool_length, 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 tool_length, 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_co;
    if (!prow){
        *p_row_num=m_row_list.size();
        *p_insert=1;
        return aim_front;
    }
    *p_insert = 0;
    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;

        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_front + tool_length)<=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;
    }
}

//=================== 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){
    tool_drag();    // ツールバー・ドラッグ
}
// ツールバー・キー押上
void DOCK_SYS::tool_on_keyup(WPARAM wp, LPARAM lp){
    tool_drag();    // ツールバー・ドラッグ
}
// マウス移動
void DOCK_SYS::tool_on_mousemove(){
    tool_drag();    // ツールバー・ドラッグ
}
// マウス左ボタン押下
void DOCK_SYS::tool_on_lbuttondown(TOOL_BAR *ptool){
    if (m_dock_context.m_now_dragging) return;
    m_dock_context.start_drag(ptool);   // ドラッグ開始

    // ツールバーを旧親ドックバーから放離させる
    if (ptool->m_parent_dock_id>=0) m_dock_bar[ptool->m_parent_dock_id].remove_tool_bar(ptool);
}
// マウス左ボタン押上
void DOCK_SYS::tool_on_lbuttonup(){
    if (!m_dock_context.m_now_dragging) return;
    m_dock_context.end_drag();  // ドラッグ終了

    RECT drag_rc;
    int dock_id, flip;
    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); // ドラッグの時のドッキング先取得

    int row_num, offset, insert;
    if (dock_id>=0) {
        // ドラッグによるドッキング位置取得
        m_dock_bar[dock_id].get_dock_pos_by_drag(&drag_rc, m_dock_context.m_p_tool_bar, &row_num, &offset, &insert);
        // ドッキング実行
        do_dock(m_dock_context.m_p_tool_bar, dock_id, row_num, offset, insert);
    }
    // フローティング実行
    else do_float(m_dock_context.m_p_tool_bar, 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_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 row_num=0, offset=0, insert;
    DOCK_BAR *pdock=&m_dock_bar[dock_id];
    DOCK_BAR_ROW *prow;
    int tool_length = (pdock->m_vh_type=='H')? ptool->m_horz_size.cx+10:ptool->m_vert_size.cy+10;
    if (ptool->m_parent_dock_id>=0) m_dock_bar[ptool->m_parent_dock_id].remove_tool_bar(ptool);

    prow=pdock->m_row_list[row_num];
    if (prow && prow->m_ptool_list.size()){
        offset=prow->m_tool_offset_list.last() + prow->m_ptool_list.last()->m_length;
    }
    offset=pdock->get_dock_offset(offset, ptool, tool_length, &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);    // ドックバーにツールバーを合加する

    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->m_length = rc.right-rc.left;
            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->m_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->m_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->m_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;
    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-04  前へ←  ホームへ  →次へ  004-06