Mesoscopic Programming

タコさんプログラミング専門

ウィンドウズプログラミング講座第14回:マウスでドラッグ

概要

ドキュメントビュー画面をマウスでドラッグできるようにしました。
ドキュメントビューにフォーカスがあるとき、スペースキーで次のファイルまたは前のファイルを選択できるようにしました。


ドキュメントビュークラス

メンバ変数と関数

class DocView : public View
{
    POINT   ptTrack;

    LRESULT OnWmMouseMove();
    LRESULT OnWmLbuttonDown();
    LRESULT OnWmLbuttonUp();
    LRESULT OnWmKeyDown();
};
解説

マウス座標保存用変数とマウスイベント関数を追加しました。

ドラッグ開始と終了

LRESULT DocView :: OnWmLbuttonDown()
{
    INT x = ( INT ) LOWORD( lparam ) + GetScrollPos( hwnd, SB_HORZ );
    INT y = ( INT ) HIWORD( lparam ) + GetScrollPos( hwnd, SB_VERT );

    if ( x >= 0 && x < docWidth && y >= 0 && y < docHeight )
    {
        GetCursorPos( & ptTrack );
        SetCursor( LoadCursor( NULL, IDC_HAND ) );
        SetCapture( hwnd );
    }

    return 0;
}

LRESULT DocView :: OnWmLbuttonUp()
{
    if ( GetCapture() == hwnd )
    {
        ReleaseCapture();
    }

    return 0;
}
解説

マウスがクリックされたら、座標が現在のドキュメントサイズ内かチェックしマウスをキャプチャしてドラッグ開始します。
ボタンが離されたら、キャプチャ解除してドラッグ終了します。

ドラッグ

LRESULT DocView :: OnWmMouseMove()
{
    POINT pt;
    INT   xmin;
    INT   xmax;
    INT   ymin;
    INT   ymax;
    INT   xpos;
    INT   ypos;

    if ( GetCapture() == hwnd )
    {
        GetCursorPos( & pt );

        GetScrollRange( hwnd, SB_HORZ, & xmin, & xmax );
        GetScrollRange( hwnd, SB_VERT, & ymin, & ymax );

        xpos = GetScrollPos( hwnd, SB_HORZ ) - ( pt.x - ptTrack.x );
        ypos = GetScrollPos( hwnd, SB_VERT ) - ( pt.y - ptTrack.y );

        if ( xpos < 0 )
        {
            xpos = 0;
        }
        else if ( xpos > xmax )
        {
            xpos = xmax;
        }
        else
        {
            ptTrack.x = pt.x;
        }

        if ( ypos < 0 )
        {
            ypos = 0;
        }
        else if ( ypos > ymax )
        {
            ypos = ymax;
        }
        else
        {
            ptTrack.y = pt.y;
        }

        Scroll( SB_HORZ, xpos );
        Scroll( SB_VERT, ypos );
    }

    return 0;
}
解説

マウスの前の座標との差分をスクロールします。

ファイル選択

LRESULT DocView :: OnWmKeyDown()
{
    UINT  key   = ( UINT ) wparam;
    SHORT state = GetKeyState( VK_SHIFT );

    switch ( key )
    {
    case VK_SPACE :

        if ( ( state & 0x8000 ) != 0 )
        {
            app.treeView.SelectPrevItem();
        }
        else
        {
            app.treeView.SelectNextItem();
        }

        return 0;
    }

    return View :: OnWmKeyDown();
}
解説

スペースキーが押されたら、ツリービューにお願いして次のファイルを選択します。
但しシフトキーも押されていたら、前のファイルを選択します。


ツリービュークラス

現在選択中の項目の次の項目、または前の項目を選択する関数を追加しました。

class TreeView : public View
{
    VOID SelectNextItem();
    VOID SelectPrevItem();
};

次の項目を選択

VOID TreeView :: SelectNextItem()
{
    HTREEITEM hItem = TreeView_GetSelection( hTree );
    HTREEITEM hItem2;

    if ( hItem != NULL )
    {
        hItem2 = TreeView_GetChild( hTree, hItem );

        if ( hItem2 != NULL )
        {
            TreeView_Select( hTree, hItem2, TVGN_CARET );
        }
        else
        {
            hItem2 = TreeView_GetNextSibling( hTree, hItem );

            if ( hItem2 != NULL )
            {
                TreeView_Select( hTree, hItem2, TVGN_CARET );
            }
            else
            {
                hItem = TreeView_GetParent( hTree, hItem );

                for ( ; hItem != NULL; )
                {
                    hItem2 = TreeView_GetNextSibling( hTree, hItem );

                    if ( hItem2 != NULL )
                    {
                        TreeView_Select( hTree, hItem2, TVGN_CARET );

                        break;
                    }

                    hItem = TreeView_GetParent( hTree, hItem );
                }
            }
        }
    }
}
解説

まず選択中の項目が子アイテムを持っている場合は、その子アイテムを選択します。
弟アイテムがある場合は、その弟アイテムを選択します。
弟アイテムが無い場合は、親の弟つまりおじさんアイテムを選択します。
おじさんアイテムが無い場合は、おじいさんの弟の…という風に見つかるまで先祖を探します。

前の項目を選択

VOID TreeView :: SelectPrevItem()
{
    HTREEITEM hItem = TreeView_GetSelection( hTree );
    HTREEITEM hItem2;

    if ( hItem != NULL )
    {
        hItem2 = TreeView_GetPrevSibling( hTree, hItem );

        if ( hItem2 != NULL )
        {
            hItem = TreeView_GetChild( hTree, hItem2 );

            if ( hItem != NULL )
            {
                for ( ; hItem != NULL; )
                {
                    hItem2 = hItem;
                    hItem  = TreeView_GetNextSibling( hTree, hItem );

                    if ( hItem == NULL )
                    {
                        hItem = TreeView_GetChild( hTree, hItem2 );
                    }
                }
            }

            TreeView_Select( hTree, hItem2, TVGN_CARET );
        }
        else
        {
            hItem2 = TreeView_GetParent( hTree, hItem );

            if ( hItem2 != NULL )
            {
                TreeView_Select( hTree, hItem2, TVGN_CARET );
            }
        }
    }
}
解説

兄アイテムが無い場合は、親アイテムを選択します。
兄アイテムがあり、その兄に子供がいない場合は、その兄アイテムを選択します。
兄が子持ちの場合は、末っ子アイテムを選択します。
但し末っ子が子持ちの場合は、その末っ子を…という風に末端の子孫までたどります。


実行画面

実行ファイル
  1. Windows.zip

以上です。