2013年8月20日火曜日

Win32APIで仮想リストビューを使ってみる

あいさつ


もともと、苦しんで覚えるC言語を読んで、試しに何か作ってみようと思ったのがWin32APIに手を出したきっかけだった。そのあと『猫でもわかるWindowsプログラミング』というのを買って読み、作ってみたのが前回のeClip風の何かと今回のfenrirとかEverything風の見た目の何か。



途中まではすいすいうまいこといってたのだが、なんかある程度自分で考えたり検索しまくったり時間をおいたりしないとわからなげな、深入りしそうな段階に入った気配がするのでいったん中断する。ただ本当にそのままほっといたら今までやったことを忘れてしまいそうなので、ここまでできたソースだけメモとしてブログにあげておきます。

コード


lview.cpp

#define ID_EDIT 101

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

#pragma comment (lib, "comctl32.lib")

typedef struct {
    TCHAR  filename[MAX_PATH];    // ファイル名
    DWORD size;                    // ファイルサイズ
    DWORD attr;                    // ファイルの属性
    TCHAR  type[256];            // 種類
    int   icon;                    // アイコン
    FILETIME writedate;            // 更新日
} filedata;

ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
HWND CreateListView(HWND hWnd);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK MyEditProc(HWND, UINT, WPARAM, LPARAM);
int DoFind(HWND hWnd);

TCHAR szClassName[] = TEXT("Cecil");  // ウィンドウクラス
HINSTANCE hInst;
HWND hMain;             // メインウィンドウのハンドル
HWND hSubList;
WNDPROC OrgEditProc;   // プロシージャアドレスを格納するための変数
static filedata *lpData = NULL;   // ファイル情報のリストを格納するための変数

// メイン
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;

    hInst = hCurInst;
    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow))
        return FALSE;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
        if (bRet == -1) {
            break;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}

// ウィンドウクラスの登録
ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;  // プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;      // インスタンス
    wc.hIcon = NULL;
    wc.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW),
                    IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;    // メニュー名
    wc.lpszClassName = szClassName;
    wc.hIconSm = NULL;

    return (RegisterClassEx(&wc));
}

// ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;

    hWnd = CreateWindow(szClassName, TEXT("Cecil"),
        WS_OVERLAPPEDWINDOW & ~WS_CAPTION, CW_USEDEFAULT,
        CW_USEDEFAULT, 500, 300, NULL, NULL, hInst, NULL);
    if (!hWnd)
        return FALSE;
    hMain = hWnd;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    static HWND edit;
    static HWND hList;

    switch (msg) {
        case WM_CREATE:
            hList = CreateListView(hWnd);
            hSubList = hList;
            ListView_SetItemState(hSubList, 0,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
            edit = CreateWindow(
                TEXT("EDIT") , NULL , 
                WS_CHILD | WS_VISIBLE | WS_BORDER ,
                0 , 0 , 0 , 0 , hWnd , (HMENU)ID_EDIT, hInst, NULL);
            // エディットコントロールをサブクラス化
            OrgEditProc = (WNDPROC)SetWindowLongPtr(
                edit, GWL_WNDPROC, (LONG)MyEditProc);
            break;
        case WM_NOTIFY:
            if (((LPNMHDR)lp)->code == LVN_GETDISPINFO) {
                TCHAR        szBuf[256];
                NMLVDISPINFO *lpDispInfo = (NMLVDISPINFO *)lp;

                switch(lpDispInfo->item.iSubItem){
                    case 0:{
                            if (lpDispInfo->item.mask & LVIF_TEXT) {
                                wsprintf(szBuf, lpData[lpDispInfo->item.iItem].filename);
                                lstrcpy(lpDispInfo->item.pszText, szBuf);
                            }
                            if (lpDispInfo->item.mask & LVIF_IMAGE) {
                                lpDispInfo->item.iImage = lpData[lpDispInfo->item.iItem].icon;
                            }        
                        }
                           break;
                    case 1:{
                            if (lpDispInfo->item.mask & LVIF_TEXT) {
                                wsprintf(szBuf, lpData[lpDispInfo->item.iItem].type);
                                lstrcpy(lpDispInfo->item.pszText, szBuf);
                            }
                            }
                            break;
                }
            }
            return 0;
        case WM_SIZE:    
            // ウィンドウサイズを調整
            MoveWindow(edit, 0, 0, LOWORD(lp), 30, TRUE);
            MoveWindow(hList, 0, 30, LOWORD(lp), HIWORD(lp), TRUE);
            break;
        case WM_SETFOCUS:
            SetFocus(edit);
            break;
        case WM_CLOSE:
             DestroyWindow(hList);
             DestroyWindow(hWnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}

// 仮想リストビューを作る
HWND CreateListView(HWND hWnd) {
    int i;
    HWND hList;
    HIMAGELIST           himl;
    SHFILEINFO           fileInfo;
    LVCOLUMN             column;
    INITCOMMONCONTROLSEX ic;
    DWORD dwStyle;

    ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
    ic.dwICC  = ICC_LISTVIEW_CLASSES;
    InitCommonControlsEx(&ic);
        
    hList = CreateWindowEx(0, WC_LISTVIEW, TEXT(""), WS_CHILD 
        | WS_VISIBLE | LVS_REPORT | LVS_OWNERDATA 
        | LVS_SHOWSELALWAYS | LVS_SINGLESEL, 
        0, 0, 0, 0, hWnd, (HMENU)ID_EDIT, hInst, NULL);
    dwStyle = ListView_GetExtendedListViewStyle(hList);
    dwStyle |= LVS_EX_FULLROWSELECT;
    ListView_SetExtendedListViewStyle(hList, dwStyle);
    column.mask    = LVCF_WIDTH | LVCF_TEXT;
    column.cx      = 300;
    column.pszText = TEXT("Name");
    ListView_InsertColumn(hList, 0, &column);
        
    column.mask    = LVCF_WIDTH | LVCF_TEXT;
    column.cx      = 200;
    column.pszText = TEXT("Type");
    ListView_InsertColumn(hList, 1, &column);

    himl = (HIMAGELIST)SHGetFileInfo((LPCTSTR)TEXT("C:\\"), 
        0, &fileInfo, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
    ListView_SetImageList(hList, himl, LVSIL_SMALL);

    i = DoFind(hList);

    ListView_SetItemCountEx(hList, i, LVSICF_NOINVALIDATEALL);

    return hList;
}

// 実行フォルダ配下のファイルを全て検索する
int DoFind(HWND hList)
{
    int             i = 0;
    int             count = 0;
    HANDLE hFind;
    WIN32_FIND_DATA fd;
    SHFILEINFO      fileInfo;
    LVITEM          item;

    /* ファイル数のカウント */
    hFind = FindFirstFile(TEXT("*.*"), &fd);
    do {

        if (lstrcmp(fd.cFileName, TEXT("..")) != 0 && lstrcmp(fd.cFileName, TEXT(".")) != 0) {
            count++;
        }
    } while(FindNextFile(hFind, &fd));

    /* カウント終了 */
    FindClose(hFind);

    /* 動的メモリ確保 */
    lpData = (filedata *)HeapAlloc(GetProcessHeap(), 0, count * sizeof(filedata));

    /* 最初のファイル検索 */
    hFind = FindFirstFile(TEXT("*.*"), &fd);

    do {

        if (lstrcmp(fd.cFileName, TEXT("..")) != 0 && lstrcmp(fd.cFileName, TEXT(".")) != 0) {
            SHGetFileInfo(fd.cFileName, 0, &fileInfo, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_TYPENAME);

            item.mask     = LVIF_TEXT | LVIF_IMAGE;
            item.iItem    = i;
            item.iSubItem = 0;
            // ファイルパス
            item.pszText  = fd.cFileName;
            lstrcpy(lpData[i].filename,fd.cFileName);
            // ファイルの属性
            lpData[i].attr = fd.dwFileAttributes;
            // 更新日
            lpData[i].writedate = fd.ftLastWriteTime;
            // ファイルタイプ
            lstrcpy(lpData[i].type, fileInfo.szTypeName);
            // アイコン    
            item.iImage   = fileInfo.iIcon;
            lpData[i].icon = fileInfo.iIcon;
            ListView_InsertItem(hList, &item);

            item.iSubItem = 1;
            item.pszText  = fd.cAlternateFileName;
            ListView_SetItem(hList, &item);

            i++;
        }
    } while(FindNextFile(hFind, &fd));
    /* 検索終了 */
    FindClose(hFind);

    return count;
}

// エディットボックスのプロシージャ
LRESULT CALLBACK MyEditProc(HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
{
    int i = 0 ,n;
    switch (msg) {
        case WM_KEYDOWN:
            if ( wp == VK_DOWN )
            {
                i = ListView_GetNextItem(hSubList,-1, LVNI_ALL | LVNI_SELECTED);

                if (i == ListView_GetItemCount(hSubList) - 1)
                {
                    ListView_SetItemState(hSubList,0,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
                    ListView_EnsureVisible(hSubList, 0, TRUE);
                }
                else
                {
                    ListView_SetItemState(hSubList,i + 1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
                    ListView_EnsureVisible(hSubList, i + 1, TRUE);
                }
                break;
            }
            else if ( wp == VK_UP )
            {
                i = ListView_GetNextItem(hSubList,-1, LVNI_ALL | LVNI_SELECTED);
                if (i == 0)
                {
                    ListView_SetItemState(hSubList,ListView_GetItemCount(hSubList) - 1,
                        LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
                    ListView_EnsureVisible(hSubList, ListView_GetItemCount(hSubList) - 1, TRUE);
                }
                else
                {
                    ListView_SetItemState(hSubList,i - 1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
                    ListView_EnsureVisible(hSubList, i - 1, TRUE);
                }
                break;
            }
            else if ( wp == VK_ESCAPE )
            {
                DestroyWindow(hEdit);
                DestroyWindow(hSubList);
                DestroyWindow(hMain);
                break;
            }
            else if ( wp == VK_RETURN )
            {
                int nItem;

                nItem = ListView_GetNextItem(
                        hSubList, -1, LVNI_ALL | LVNI_SELECTED);
                ShellExecute(NULL, TEXT("open"), lpData[nItem].filename, NULL, NULL, SW_SHOW);
                break;
            }
            return 0;
        case WM_CHAR:           // 文字入力が発生したとき
            for (n = 0; n < ListView_GetItemCount(hSubList); n++){
                ;
            }
            break;
    }

    return CallWindowProc(OrgEditProc, hEdit, msg, wp, lp);
}

説明とか


Visual C++ 2008 Express Editionのインストールの「cppファイルにコードを書く」過程で、上のコードをコピペしたら多分動くと思う。

コントロールの作成


エディットボックスとリストコントロールを作成。
リストコントロールは仮想リストビューを使っている。データの管理と描画を分離するのがこっちで、処理が早いらしい。

エディットボックスのキーバインド


エディットボックスでカーソル上下をしたら、リストコントロールのカーソルが上下するように設定している。
また、Enterを押したらデフォルトの実行。Escでプログラムを閉じる。

アイテムの登録


実行ファイルのあるフォルダのパスを取得し、表示させている。

次にやること


  • ファイルから一行ずつ読み込み
  • ある文字列にある文字列が含まれているかどうかを調べる

あたりです。また気が向いたら続きしよう。

0 件のコメント:

コメントを投稿