mity / mctrl

C library providing set of additional user interface controls for Windows, intended to be complementary to standard Win32API controls from USER32.DLL and COMCTL32.DLL.
http://mctrl.org
229 stars 50 forks source link

[Feature Request] Filter and Sort by column in the grid #76

Open bencz opened 4 years ago

bencz commented 4 years ago

I would like to use the filter system by column and sorting in the grid

Here, I have a small sample of how to use the ListView Filter:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <commctrl.h>
#include <string.h>
#include <stdbool.h>

#define WND_CLASS_NAME "CSVVIEW"
#define ID_LISTVIEW    0x8801

char* filename;
HWND  hList, hHeader;
static NMLISTVIEW* nmListView;
static NMHEADER* nmHeader;

int rgb(int r, int g, int b) { return r + (g << 8) + (b << 16); }

bool startsWith(const char* pre, const char* str)
{
    size_t lenpre = strlen(pre),
        lenstr = strlen(str);
    return lenstr < lenpre ? false : memcmp(pre, str, lenpre) == 0;
}

void onCreate(HWND hWnd, LPARAM lParam) {
    LVCOLUMN col;
    LVITEM item;
    int iCol, iCount, dwStyle, dwHeaderStyle;
    CREATESTRUCT* lp = (CREATESTRUCT*)lParam;
    FILE* fp;
    char buf[256];
    char* field;

    memset(&item, 0, sizeof(item));
    hList = CreateWindowEx(0, WC_LISTVIEW, 0, WS_CHILD | WS_VISIBLE | LVS_REPORT, 0, 0, 100, 100, hWnd, (HMENU)ID_LISTVIEW, lp->hInstance, NULL);
    dwStyle = SendMessage(hList, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
    dwStyle = dwStyle | LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP | LVS_EX_FLATSB;
    SendMessage(hList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
    col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;

    nmListView = (NMLISTVIEW*)hList;

    // TEST HEADER

    hHeader = ListView_GetHeader(hList);
    SetWindowLong(hHeader, GWL_ID, ID_LISTVIEW + 1);

    Header_SetFilterChangeTimeout(hHeader, 1000);
    dwHeaderStyle = (GetWindowLongPtr(hHeader, GWL_STYLE) | HDS_FILTERBAR) & ~HDS_BUTTONS;
    SetWindowLong(hHeader, GWL_STYLE, dwHeaderStyle);

    nmHeader = (NMHEADER*)hHeader;
    // END TEST

    fp = fopen(filename, "r");
    if (fp == NULL) {
        sprintf(buf, "Fail to open the file...", filename);
        MessageBox(hWnd, buf, "Error", MB_OK);
        exit(1);
    }
    fgets(buf, 255, fp);
    field = strtok(buf, ",");
    for (iCol = 0; field != NULL; iCol++) {
        if (field[1] != ':') { col.fmt = LVCFMT_LEFT; }
        else if (field[0] == 'l') { col.fmt = LVCFMT_LEFT;   field += 2; }
        else if (field[0] == 'c') { col.fmt = LVCFMT_CENTER; field += 2; }
        else if (field[0] == 'r') { col.fmt = LVCFMT_RIGHT;  field += 2; }
        col.cx = strlen(field) * 13;
        col.iSubItem = iCol;
        col.pszText = field;
        SendMessage(hList, LVM_INSERTCOLUMN, iCol, (LPARAM)&col);
        field = strtok(NULL, ",\n");
    }

    item.mask = LVIF_TEXT;
    for (iCount = 0; fgets(buf, 255, fp) != NULL; iCount++) {
        item.pszText = strtok(buf, ",");
        item.iItem = iCount;
        item.iSubItem = 0;
        SendMessage(hList, LVM_INSERTITEM, 0, (LPARAM)&item);
        for (iCol = 1; (field = strtok(NULL, ",\n")) != NULL; iCol++) {
            item.pszText = field;
            item.iItem = iCount;
            item.iSubItem = iCol;
            SendMessage(hList, LVM_SETITEM, 0, (LPARAM)&item);
        }
    }
}

void onSize(HWND hwnd, WPARAM wp, LPARAM lp) {
    RECT rc;
    GetClientRect(hwnd, &rc);
    MoveWindow(hList, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE);
}

int onNotify(HWND hWnd, WPARAM wp, LPARAM lp) {
    NMHDR* pnmhdr = (NMHDR*)lp;

    if (pnmhdr->code == NM_CUSTOMDRAW)
    {
        NMLVCUSTOMDRAW* lpCustomDraw = (NMLVCUSTOMDRAW*)pnmhdr;
        switch (lpCustomDraw->nmcd.dwDrawStage) {
        case CDDS_PREPAINT:
            return CDRF_NOTIFYITEMDRAW;
        case CDDS_ITEMPREPAINT:
            if (lpCustomDraw->nmcd.dwItemSpec % 2 == 1)
                lpCustomDraw->clrTextBk = rgb(209, 240, 179);
            else
                lpCustomDraw->clrTextBk = rgb(250, 250, 250);
            return CDRF_NEWFONT;
        }
    }

    switch (pnmhdr->code)
    {
    case HDN_FILTERCHANGE:
    {

        HDITEM        hditem;
        HDTEXTFILTER  hdTFilter;
        TCHAR         text[260];
        LRESULT       size;

        NMLISTVIEW* nmListView = (NMLISTVIEW*)GetDlgCtrlID(pnmhdr->hwndFrom);

        //HWND hHeaderTmp = ListView_GetHeader(hList);
        for (int i = 0; i < SendMessageW(hHeader, HDM_GETITEMCOUNT, 0, 0); i++) {
            memset(&hditem, 0, sizeof(HDITEM));
            memset(text,   0, 260);

            hditem.mask = (HDI_FILTER);
            hdTFilter.pszText = text;
            hdTFilter.cchTextMax = 260;
            hditem.type = HDFT_ISSTRING;
            hditem.pvFilter = &hdTFilter;

            SendMessageW(hHeader, HDM_GETITEM, i, (LPARAM)&hditem);

            if (strlen(text) > 0)
            {
                char buf[256];
                char* field;
                LVCOLUMN col;
                LVITEM item;

                ListView_DeleteAllItems(hList);

                FILE* fp = fopen(filename, "r");
                if (fp == NULL) {
                    sprintf(buf, "Fail to open the file...", filename);
                    MessageBox(hWnd, buf, "Error", MB_OK);
                    exit(1);
                }
                fgets(buf, 255, fp); // pula a primeira linha...
                item.mask = LVIF_TEXT;
                for (int iCount = 0; fgets(buf, 255, fp) != NULL; iCount++)
                {
                    char* resultTok = strtok(buf, ",");
                    if (!startsWith(text, resultTok))
                        continue;

                    item.pszText = resultTok;
                    item.iItem = iCount;
                    item.iSubItem = 0;

                    SendMessage(hList, LVM_INSERTITEM, 0, (LPARAM)&item);
                    for (int iCol = 1; (field = strtok(NULL, ",\n")) != NULL; iCol++) {
                        item.pszText = field;
                        item.iItem = iCount;
                        item.iSubItem = iCol;
                        SendMessage(hList, LVM_SETITEM, 0, (LPARAM)&item);
                    }
                }

                break;
            }
        }
    }
    break;
    case HDN_FILTERBTNCLICK:
        //MessageBox(hWnd, "otro_oi", "Error", MB_OK);
        break;
    default:
        break;
    }

    return DefWindowProc(hWnd, WM_NOTIFY, wp, lp);
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_CREATE:  onCreate(hWnd, lParam);  break;
    case WM_SIZE:    onSize(hWnd, wParam, lParam);  break;
    case WM_NOTIFY:  return onNotify(hWnd, wParam, lParam);
    case WM_DESTROY: PostQuitMessage(0);  break;
    default: return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    char buf[256];
    HWND hWnd;
    WNDCLASS wcl;
    MSG msg;

    filename = lpCmdLine;
    if (filename == NULL) return FALSE;
    InitCommonControls();

    memset(&wcl, 0, sizeof(wcl));
    wcl.hInstance = hInstance;
    wcl.lpszClassName = WND_CLASS_NAME;
    wcl.lpfnWndProc = WindowProc;
    wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcl.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    if (!RegisterClass(&wcl)) return FALSE;

    wsprintf(buf, "%s - %s", "CSV View", lpCmdLine);
    hWnd = CreateWindowEx(0, WND_CLASS_NAME, buf, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 900, 300, NULL, NULL, hInstance, NULL);
    if (!hWnd) return FALSE;

    //char bufs[256];
    //sprintf(bufs, "%d                            %x", LVM_GETHEADER);
    //MessageBox(hWnd, bufs, "Error", MB_OK);

    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}