diff --git a/wintable/accessibility_darwin.c b/wintable/accessibility_darwin.c deleted file mode 100644 index d72524c..0000000 --- a/wintable/accessibility_darwin.c +++ /dev/null @@ -1,117 +0,0 @@ -// 9 november 2014 -#define UNICODE -#define _UNICODE -#define STRICT -#define STRICT_TYPED_ITEMIDS -#define CINTERFACE -// get Windows version right; right now Windows XP -#define WINVER 0x0501 -#define _WIN32_WINNT 0x0501 -#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */ -#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */ -#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */ -#include <windows.h> -#include <commctrl.h> -#include <stdint.h> -#include <uxtheme.h> -#include <string.h> -#include <wchar.h> -#include <windowsx.h> -#include <vsstyle.h> -#include <vssym32.h> -#include <oleacc.h> - -struct tableAccessible { - IAccessibleVtbl vtbl; - volatile ULONG refcount; // TODO ensure this is aligned - struct table *t; -}; - -static IAccessibleVtbl aaccessible = { - // IUnknkown - .QueryInterface = taQueryInterface, - .AddRef = taAddRef, - .Release = taRelease, - // IDispatch - .GetTypeInfoCount = taGetTypeInfoCount, - .GetTypeInfo = taGetTypeInfo, - .GetIDsOfNames = taGetIDsOfNames, - .Invoke = taInvoke, - // IAccessible - ... -}; - -HRESULT STDMETHODCALLTYPE taQueryInterface(IUnknown *this, REFIID riid, void **ppvObject) -{ - if (ppvObject == NULL) - return E_POINTER; - // we're required to return the same pointer for IUnknown - // since this is a straight singly-derived interface inheritance, we can exploit the structure layout and just return the same pointer for everything - // at least I hope... (TODO) - if (IsEqualIID(riid, IID_IUnknown) || - IsEqualIID(riid, IID_IDispatch) || - IsEqualIID(riid, IID_IAccessible)) { - this->AddRef(this); - *ppvObject = (void *) this; - return S_OK; - } - // we're not making a special class for this - *ppvObject = NULL; - return E_NOINTERFACE; -} - -ULONG STDMETHODCALLTYPE taAddRef(IUnknown *this) -{ - // TODO is the signed conversion safe? - return (ULONG) InterlockedIncrement((volatile LONG *) (&(((tableAccessible *) this)->refcount))); -} - -ULONG STDMETHODCALLTYPE taRelease(IUnknown *this) -{ - ULONG rc; - - rc = (ULONG) InterlockedDecrement((volatile LONG *) (&(((tableAccessible *) this)->refcount))); - // don't pull the refcount back out (see http://blogs.msdn.com/b/oldnewthing/archive/2013/04/25/10413997.aspx) - if (rc == 0) - free((tableAccessible *) this); - return rc; -} - -// here's the IDispatch member functions -// we actually /don't/ need to define any of these! -// see also http://msdn.microsoft.com/en-us/library/windows/desktop/cc307844.aspx - -HRESULT STDMETHODCALLTYPE taGetTypeInfoCount(IDispatch *this, UINT *pctinfo) -{ - if (pctinfo == NULL) - return E_INVALIDARG; - // TODO really set this to zero? - *pctinfo = 0; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE taGetTypeInfo(IDispatch *this, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) -{ - if (pctinfo == NULL) - return E_INVALIDARG; - *ppTInfo = NULL; - // let's do this just to be safe - if (iTInfo == 0) - return DISP_E_BADINDEX; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE taGetIDsOfNames(IDispatch *this, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) -{ - // rgDispId is an array of LONGs; setting it to NULL is useless - // TODO should we clear the array? - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE taInvoke(IDispatch *this, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) -{ - // TODO set anything to NULL or 0? - return E_NOTIMPL; -} - -// ok that's it for IDispatch; now for IAccessible! diff --git a/wintable/api.h b/wintable/api.h deleted file mode 100644 index 5fdf599..0000000 --- a/wintable/api.h +++ /dev/null @@ -1,54 +0,0 @@ -// 29 november 2014 - -static void addColumn(struct table *t, WPARAM wParam, LPARAM lParam) -{ - HDITEMW item; - - if (((int) wParam) >= nTableColumnTypes) - abort(); - - t->nColumns++; - t->columnTypes = (int *) realloc(t->columnTypes, t->nColumns * sizeof (int)); - if (t->columnTypes == NULL) - abort(); - t->columnTypes[t->nColumns - 1] = (int) wParam; - - ZeroMemory(&item, sizeof (HDITEMW)); - item.mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT; - item.cxy = 200; // TODO - item.pszText = (WCHAR *) lParam; - item.fmt = HDF_LEFT | HDF_STRING; - if (SendMessage(t->header, HDM_INSERTITEM, (WPARAM) (t->nColumns - 1), (LPARAM) (&item)) == (LRESULT) (-1)) - abort(); - // TODO resize(t)? - redrawAll(t); -} - -HANDLER(API) -{ - switch (uMsg) { - case WM_SETFONT: - t->font = (HFONT) wParam; - if (t->font == NULL) - t->font = t->defaultFont; - // also set the header font - SendMessageW(t->header, WM_SETFONT, wParam, lParam); - if (LOWORD(lParam) != FALSE) { - // the scrollbar page size will change so redraw that too - // also recalculate the header height - // TODO do that when this is FALSE too somehow - resize(t); - redrawAll(t); - } - *lResult = 0; - return TRUE; - case WM_GETFONT: - *lResult = (LRESULT) t->font; - return TRUE; - case tableAddColumn: - addColumn(t, wParam, lParam); - *lResult = 0; - return TRUE; - } - return FALSE; -} diff --git a/wintable/draw.h b/wintable/draw.h deleted file mode 100644 index adc1e68..0000000 --- a/wintable/draw.h +++ /dev/null @@ -1,166 +0,0 @@ -// 30 november 2014 - -static void resize(struct table *t) -{ - RECT r; - SCROLLINFO si; - - // do this first so our scrollbar calculations can be correct - repositionHeader(t); - - // now adjust the scrollbars - r = realClientRect(t); - t->pagesize = (r.bottom - r.top) / rowHeight(t); - ZeroMemory(&si, sizeof (SCROLLINFO)); - si.cbSize = sizeof (SCROLLINFO); - si.fMask = SIF_RANGE | SIF_PAGE; - si.nMin = 0; - si.nMax = t->count - 1; - si.nPage = t->pagesize; - SetScrollInfo(t->hwnd, SB_VERT, &si, TRUE); - - recomputeHScroll(t); -} - -// TODO alter this so that only the visible columns are redrawn -// TODO this means rename controlSize to clientRect -static void drawItem(struct table *t, HDC dc, intptr_t i, LONG y, LONG height, RECT controlSize) -{ - RECT rsel; - HBRUSH background; - int textColor; - WCHAR msg[100]; - RECT headeritem; - intptr_t j; - LRESULT xoff; - IMAGELISTDRAWPARAMS ip; - POINT pt; - - // TODO verify these two - background = (HBRUSH) (COLOR_WINDOW + 1); - textColor = COLOR_WINDOWTEXT; - if (t->selected == i) { - // these are the colors wine uses (http://source.winehq.org/source/dlls/comctl32/listview.c) - // the two for unfocused are also suggested by http://stackoverflow.com/questions/10428710/windows-forms-inactive-highlight-color - background = (HBRUSH) (COLOR_HIGHLIGHT + 1); - textColor = COLOR_HIGHLIGHTTEXT; - if (GetFocus() != t->hwnd) { - background = (HBRUSH) (COLOR_BTNFACE + 1); - textColor = COLOR_BTNTEXT; - } - } - - // first fill the selection rect - // note that this already only draws the visible area - rsel.left = controlSize.left; - rsel.top = y; - rsel.right = controlSize.right - controlSize.left; - rsel.bottom = y + height; - if (FillRect(dc, &rsel, background) == 0) - abort(); - - // TODO double-check to see if this takes any parameters - xoff = SendMessageW(t->header, HDM_GETBITMAPMARGIN, 0, 0); - // now adjust for horizontal scrolling - xoff -= t->hpos; - - // now draw the cells - if (SetTextColor(dc, GetSysColor(textColor)) == CLR_INVALID) - abort(); - if (SetBkMode(dc, TRANSPARENT) == 0) - abort(); - for (j = 0; j < t->nColumns; j++) { - if (SendMessageW(t->header, HDM_GETITEMRECT, (WPARAM) j, (LPARAM) (&headeritem)) == 0) - abort(); - switch (t->columnTypes[j]) { - case tableColumnText: - rsel.left = headeritem.left + xoff; - rsel.top = y; - rsel.right = headeritem.right; - rsel.bottom = y + height; - // TODO vertical center in case the height is less than the icon height? - if (DrawTextExW(dc, msg, wsprintf(msg, L"Item %d", i), &rsel, DT_END_ELLIPSIS | DT_LEFT | DT_NOPREFIX | DT_SINGLELINE, NULL) == 0) - abort(); - break; - case tableColumnImage: - // TODO vertically center if image is smaller than text height - // TODO same for checkboxes - ZeroMemory(&ip, sizeof (IMAGELISTDRAWPARAMS)); - ip.cbSize = sizeof (IMAGELISTDRAWPARAMS); - ip.himl = t->checkboxes;//t->imagelist; - ip.i = (i%8);//0; - ip.hdcDst = dc; - ip.x = headeritem.left + xoff; - ip.y = y; - ip.cx = 0; // draw whole image - ip.cy = 0; - ip.xBitmap = 0; - ip.yBitmap = 0; - ip.rgbBk = CLR_NONE; - ip.fStyle = ILD_NORMAL | ILD_SCALE; // TODO alpha-blend; ILD_DPISCALE? - // TODO ILS_ALPHA? - if (ImageList_DrawIndirect(&ip) == 0) - abort(); - break; - case tableColumnCheckbox: - // TODO - break; - } - if (t->selected == i && t->focusedColumn == j) { - rsel.left = headeritem.left; - rsel.top = y; - rsel.right = headeritem.right; - rsel.bottom = y + height; - if (DrawFocusRect(dc, &rsel) == 0) - abort(); - } - } -} - -static void drawItems(struct table *t, HDC dc, RECT cliprect) -{ - HFONT thisfont, prevfont; - LONG height; - LONG y; - intptr_t i; - RECT controlSize; // for filling the entire selected row - intptr_t first, last; - - if (GetClientRect(t->hwnd, &controlSize) == 0) - abort(); - - height = rowHeight(t); - - thisfont = t->font; // in case WM_SETFONT happens before we return - prevfont = (HFONT) SelectObject(dc, thisfont); - if (prevfont == NULL) - abort(); - - // ignore anything beneath the header - if (cliprect.top < t->headerHeight) - cliprect.top = t->headerHeight; - // now let's pretend the header isn't there - // we only need it in (or rather, before) the drawItem() calls below - cliprect.top -= t->headerHeight; - cliprect.bottom -= t->headerHeight; - - // see http://blogs.msdn.com/b/oldnewthing/archive/2003/07/29/54591.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/07/30/54600.aspx - // we need to add t->firstVisible here because cliprect is relative to the visible area - first = (cliprect.top / height) + t->firstVisible; - if (first < 0) - first = 0; - last = lastVisible(t, cliprect, height); - - // now for the first y, discount firstVisible - y = (first - t->firstVisible) * height; - // and offset by the header height - y += t->headerHeight; - for (i = first; i < last; i++) { - drawItem(t, dc, i, y, height, controlSize); - y += height; - } - - // reset everything - if (SelectObject(dc, prevfont) != (HGDIOBJ) (thisfont)) - abort(); -} diff --git a/wintable/hscroll.h b/wintable/hscroll.h deleted file mode 100644 index f4c3b0f..0000000 --- a/wintable/hscroll.h +++ /dev/null @@ -1,125 +0,0 @@ -// 29 november 2014 - -static void hscrollto(struct table *t, intptr_t newpos) -{ - SCROLLINFO si; - RECT scrollArea; - - if (newpos < 0) - newpos = 0; - if (newpos > (t->width - t->hpagesize)) - newpos = (t->width - t->hpagesize); - - scrollArea = realClientRect(t); - - // negative because ScrollWindowEx() is "backwards" - if (ScrollWindowEx(t->hwnd, -(newpos - t->hpos), 0, - &scrollArea, &scrollArea, NULL, NULL, - SW_ERASE | SW_INVALIDATE) == ERROR) - abort(); - t->hpos = newpos; - // TODO text in header controls doesn't redraw? - - // TODO put this in a separate function? same for vscroll? - ZeroMemory(&si, sizeof (SCROLLINFO)); - si.cbSize = sizeof (SCROLLINFO); - si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; - si.nPage = t->hpagesize; - si.nMin = 0; - si.nMax = t->width - 1; // nMax is inclusive - si.nPos = t->hpos; - SetScrollInfo(t->hwnd, SB_HORZ, &si, TRUE); - - // and finally reposition the header - repositionHeader(t); -} - -static void hscrollby(struct table *t, intptr_t n) -{ - hscrollto(t, t->hpos + n); -} - -// unfortunately horizontal wheel scrolling was only added in Vista - -static void hscroll(struct table *t, WPARAM wParam) -{ - SCROLLINFO si; - intptr_t newpos; - - ZeroMemory(&si, sizeof (SCROLLINFO)); - si.cbSize = sizeof (SCROLLINFO); - si.fMask = SIF_POS | SIF_TRACKPOS; - if (GetScrollInfo(t->hwnd, SB_HORZ, &si) == 0) - abort(); - - newpos = t->hpos; - switch (LOWORD(wParam)) { - case SB_LEFT: - newpos = 0; - break; - case SB_RIGHT: - newpos = t->width - t->hpagesize; - break; - case SB_LINELEFT: - newpos--; - break; - case SB_LINERIGHT: - newpos++; - break; - case SB_PAGELEFT: - newpos -= t->hpagesize; - break; - case SB_PAGERIGHT: - newpos += t->hpagesize; - break; - case SB_THUMBPOSITION: - newpos = (intptr_t) (si.nPos); - break; - case SB_THUMBTRACK: - newpos = (intptr_t) (si.nTrackPos); - } - - hscrollto(t, newpos); -} - -static void recomputeHScroll(struct table *t) -{ - HDITEMW item; - intptr_t i; - int width = 0; - RECT r; - SCROLLINFO si; - - // TODO count dividers - for (i = 0; i < t->nColumns; i++) { - ZeroMemory(&item, sizeof (HDITEMW)); - item.mask = HDI_WIDTH; - if (SendMessageW(t->header, HDM_GETITEM, (WPARAM) i, (LPARAM) (&item)) == FALSE) - abort(); - width += item.cxy; - } - t->width = (intptr_t) width; - - if (GetClientRect(t->hwnd, &r) == 0) - abort(); - t->hpagesize = r.right - r.left; - - ZeroMemory(&si, sizeof (SCROLLINFO)); - si.cbSize = sizeof (SCROLLINFO); - si.fMask = SIF_PAGE | SIF_RANGE; - si.nPage = t->hpagesize; - si.nMin = 0; - si.nMax = t->width - 1; // - 1 because endpoints inclusive - SetScrollInfo(t->hwnd, SB_HORZ, &si, TRUE); -} - -HANDLER(hscroll) -{ - switch (uMsg) { - case WM_HSCROLL: - hscroll(t, wParam); - *lResult = 0; - return TRUE; - } - return FALSE; -} diff --git a/wintable/imagelist_windows.c b/wintable/imagelist_windows.c deleted file mode 100644 index e194f49..0000000 --- a/wintable/imagelist_windows.c +++ /dev/null @@ -1,195 +0,0 @@ -// 16 august 2014 - -#define UNICODE -#define _UNICODE -#define STRICT -#define STRICT_TYPED_ITEMIDS -#define CINTERFACE -// get Windows version right; right now Windows XP -#define WINVER 0x0501 -#define _WIN32_WINNT 0x0501 -#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */ -#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */ -#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */ -#include <windows.h> -#include <commctrl.h> -#include <stdint.h> -#include <uxtheme.h> -#include <string.h> -#include <wchar.h> -#include <windowsx.h> -#include <vsstyle.h> -#include <vssym32.h> -#include <oleacc.h> - -enum { - checkboxStateChecked = 1 << 0, - checkboxStateHot = 1 << 1, - checkboxStatePushed = 1 << 2, - checkboxnStates = 1 << 3, -}; - -#define xpanic(...) abort() -#define xpanichresult(...) abort() - -static UINT dfcState(int cbstate) -{ - UINT ret; - - ret = DFCS_BUTTONCHECK; - if ((cbstate & checkboxStateChecked) != 0) - ret |= DFCS_CHECKED; - if ((cbstate & checkboxStateHot) != 0) - ret |= DFCS_HOT; - if ((cbstate & checkboxStatePushed) != 0) - ret |= DFCS_PUSHED; - return ret; -} - -static void dfcImage(HDC dc, RECT *r, int cbState, HTHEME theme) -{ - if (DrawFrameControl(dc, r, DFC_BUTTON, dfcState(cbState)) == 0) - xpanic("error drawing checkbox image", GetLastError()); -} - -static void dfcSize(HDC dc, int *width, int *height, HTHEME theme) -{ - // there's no real metric around - // let's use SM_CX/YSMICON and hope for the best - *width = GetSystemMetrics(SM_CXSMICON); - *height = GetSystemMetrics(SM_CYSMICON); -} - -static int themestates[checkboxnStates] = { - CBS_UNCHECKEDNORMAL, // 0 - CBS_CHECKEDNORMAL, // checked - CBS_UNCHECKEDHOT, // hot - CBS_CHECKEDHOT, // checked | hot - CBS_UNCHECKEDPRESSED, // pushed - CBS_CHECKEDPRESSED, // checked | pushed - CBS_UNCHECKEDPRESSED, // hot | pushed - CBS_CHECKEDPRESSED, // checked | hot | pushed -}; - -static SIZE getStateSize(HDC dc, int cbState, HTHEME theme) -{ - SIZE s; - HRESULT res; - - res = GetThemePartSize(theme, dc, BP_CHECKBOX, themestates[cbState], NULL, TS_DRAW, &s); - if (res != S_OK) - xpanichresult("error getting theme part size", res); - return s; -} - -static void themeImage(HDC dc, RECT *r, int cbState, HTHEME theme) -{ - HRESULT res; - - res = DrawThemeBackground(theme, dc, BP_CHECKBOX, themestates[cbState], r, NULL); - if (res != S_OK) - xpanichresult("error drawing checkbox image", res); -} - -static void themeSize(HDC dc, int *width, int *height, HTHEME theme) -{ - SIZE size; - int cbState; - - size = getStateSize(dc, 0, theme); - for (cbState = 1; cbState < checkboxnStates; cbState++) { - SIZE against; - - against = getStateSize(dc, cbState, theme); - if (size.cx != against.cx || size.cy != against.cy) - xpanic("size mismatch in checkbox states", GetLastError()); - } - *width = (int) size.cx; - *height = (int) size.cy; -} - -static HBITMAP makeCheckboxImageListEntry(HDC dc, int width, int height, int cbState, void (*drawfunc)(HDC, RECT *, int, HTHEME), HTHEME theme) -{ - BITMAPINFO bi; - VOID *ppvBits; - HBITMAP bitmap; - RECT r; - HDC drawDC; - HBITMAP prevbitmap; - - r.left = 0; - r.top = 0; - r.right = width; - r.bottom = height; - ZeroMemory(&bi, sizeof (BITMAPINFO)); - bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - bi.bmiHeader.biWidth = (LONG) width; - bi.bmiHeader.biHeight = -((LONG) height); // negative height to force top-down drawing; - bi.bmiHeader.biPlanes = 1; - bi.bmiHeader.biBitCount = 32; - bi.bmiHeader.biCompression = BI_RGB; - bi.bmiHeader.biSizeImage = (DWORD) (width * height * 4); - bitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &ppvBits, 0, 0); - if (bitmap == NULL) - xpanic("error creating HBITMAP for unscaled ImageList image copy", GetLastError()); - - drawDC = CreateCompatibleDC(dc); - if (drawDC == NULL) - xpanic("error getting DC for checkbox image list bitmap", GetLastError()); - prevbitmap = SelectObject(drawDC, bitmap); - if (prevbitmap == NULL) - xpanic("error selecting checkbox image list bitmap into DC", GetLastError()); - (*drawfunc)(drawDC, &r, cbState, theme); - if (SelectObject(drawDC, prevbitmap) != bitmap) - xpanic("error selecting previous bitmap into checkbox image's DC", GetLastError()); - if (DeleteDC(drawDC) == 0) - xpanic("error deleting checkbox image's DC", GetLastError()); - - return bitmap; -} - -static HIMAGELIST newCheckboxImageList(HWND hwnddc, void (*sizefunc)(HDC, int *, int *, HTHEME), void (*drawfunc)(HDC, RECT *, int, HTHEME), HTHEME theme, int *width, int *height) -{ - int cbState; - HDC dc; - HIMAGELIST il; - - dc = GetDC(hwnddc); - if (dc == NULL) - xpanic("error getting DC for making the checkbox image list", GetLastError()); - (*sizefunc)(dc, width, height, theme); - il = ImageList_Create(*width, *height, ILC_COLOR32, 20, 20); // should be reasonable - if (il == NULL) - xpanic("error creating checkbox image list", GetLastError()); - for (cbState = 0; cbState < checkboxnStates; cbState++) { - HBITMAP bitmap; - - bitmap = makeCheckboxImageListEntry(dc, *width, *height, cbState, drawfunc, theme); - if (ImageList_Add(il, bitmap, NULL) == -1) - xpanic("error adding checkbox image to image list", GetLastError()); - if (DeleteObject(bitmap) == 0) - xpanic("error deleting checkbox bitmap", GetLastError()); - } - if (ReleaseDC(hwnddc, dc) == 0) - xpanic("error deleting checkbox image list DC", GetLastError()); - return il; -} - -HIMAGELIST makeCheckboxImageList(HWND hwnddc, HTHEME *theme, int *width, int *height) -{ - if (*theme != NULL) { - HRESULT res; - - res = CloseThemeData(*theme); - if (res != S_OK) - xpanichresult("error closing theme", res); - *theme = NULL; - } - // ignore error; if it can't be done, we can fall back to DrawFrameControl() - if (*theme == NULL) // try to open the theme - *theme = OpenThemeData(hwnddc, L"button"); - if (*theme != NULL) // use the theme - return newCheckboxImageList(hwnddc, themeSize, themeImage, *theme, width, height); - // couldn't open; fall back - return newCheckboxImageList(hwnddc, dfcSize, dfcImage, *theme, width, height); -} diff --git a/wintable/main.c b/wintable/main.c deleted file mode 100644 index 4f1b3e2..0000000 --- a/wintable/main.c +++ /dev/null @@ -1,303 +0,0 @@ -// 19 october 2014 -#define UNICODE -#define _UNICODE -#define STRICT -#define STRICT_TYPED_ITEMIDS -#define CINTERFACE -// get Windows version right; right now Windows XP -#define WINVER 0x0501 -#define _WIN32_WINNT 0x0501 -#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */ -#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */ -#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */ -#include <windows.h> -#include <commctrl.h> -#include <stdint.h> -#include <uxtheme.h> -#include <string.h> -#include <wchar.h> -extern HIMAGELIST makeCheckboxImageList(HWND hwnddc, HTHEME *theme, int *, int *); -enum { - checkboxStateChecked = 1 << 0, - checkboxStateHot = 1 << 1, - checkboxStatePushed = 1 << 2, - checkboxnStates = 1 << 3, -}; -#include <windowsx.h> -#include <vsstyle.h> -#include <vssym32.h> -#include <oleacc.h> - -// #qo LIBS: user32 kernel32 gdi32 comctl32 uxtheme - -// TODO -// - http://blogs.msdn.com/b/oldnewthing/archive/2003/09/09/54826.aspx (relies on the integrality parts? IDK) -// - might want to http://blogs.msdn.com/b/oldnewthing/archive/2003/09/17/54944.aspx instead -// - http://msdn.microsoft.com/en-us/library/windows/desktop/bb775574%28v=vs.85%29.aspx -// - hscroll -// - keyboard navigation -// - how will this affect hot-tracking? -// - automatic hscroll when scrolling columns -// - accessibility -// - must use MSAA as UI Automation is not included by default on Windows XP (and apparently requires SP3?) -// - try horizontally scrolling the initail window and watch the selection rect corrupt itself *sometimes* -// - preallocate t->columnTypes instead of keeping it at exactly the right size -// - checkbox events -// - space to toggle (TODO); + or = to set; - to clear (see http://msdn.microsoft.com/en-us/library/windows/desktop/bb775941%28v=vs.85%29.aspx) -// - TODO figure out which notification is needed -// - http://blogs.msdn.com/b/oldnewthing/archive/2006/01/03/508694.aspx -// - free all allocated resources on WM_DESTROY -// - rename lastmouse -// - or perhaps do a general cleanup of the checkbox and mouse event code... -// - figure out why initial draw pretends there is no header -// - find places where the top-left corner of the client rect is assumed to be (0, 0) -// - rewrite a lot of this crap to make better sense of coordinates and item counts, get rid of needless resizing and redrawing, etc. - -#define tableWindowClass L"gouitable" - -// start at WM_USER + 20 just in case for whatever reason we ever get the various dialog manager messages (see also http://blogs.msdn.com/b/oldnewthing/archive/2003/10/21/55384.aspx) -enum { - // wParam - one of the type constants - // lParam - column name as a Unicode string - tableAddColumn = WM_USER + 20, -}; - -enum { - tableColumnText, - tableColumnImage, - tableColumnCheckbox, - nTableColumnTypes, -}; - -struct table { - HWND hwnd; - HFONT defaultFont; - HFONT font; - intptr_t selected; - intptr_t count; - intptr_t firstVisible; - intptr_t pagesize; // in rows - int wheelCarry; - HWND header; - int headerHeight; - intptr_t nColumns; - HIMAGELIST imagelist; - int imagelistHeight; - intptr_t width; - intptr_t hpagesize; - intptr_t hpos; - HIMAGELIST checkboxes; - HTHEME theme; - int *columnTypes; - intptr_t focusedColumn; - int checkboxWidth; - int checkboxHeight; -}; - -#define HANDLER(what) static BOOL what ## Handler(struct table *t, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult) -#include "util.h" -#include "hscroll.h" -#include "vscroll.h" -#include "selection.h" -#include "draw.h" -#include "api.h" - -typedef BOOL (*handlerfunc)(struct table *, UINT, WPARAM, LPARAM, LRESULT *); - -const handlerfunc handlerfuncs[] = { - hscrollHandler, - vscrollHandler, - APIHandler, - NULL, -}; - -// TODO create a system where each of the above modules provide their own window procedures -static LRESULT CALLBACK tableWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - struct table *t; - handlerfunc *hf; - LRESULT lResult; - HDC dc; - PAINTSTRUCT ps; - NMHDR *nmhdr = (NMHDR *) lParam; - NMHEADERW *nm = (NMHEADERW *) lParam; - - t = (struct table *) GetWindowLongPtrW(hwnd, GWLP_USERDATA); - if (t == NULL) { - // we have to do things this way because creating the header control will fail mysteriously if we create it first thing - // (which is fine; we can get the parent hInstance this way too) - if (uMsg == WM_NCCREATE) { - CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; - - t = (struct table *) malloc(sizeof (struct table)); - if (t == NULL) - abort(); - ZeroMemory(t, sizeof (struct table)); - t->hwnd = hwnd; - // TODO this should be a global - t->defaultFont = (HFONT) GetStockObject(SYSTEM_FONT); - if (t->defaultFont == NULL) - abort(); - t->font = t->defaultFont; -t->selected = 5;t->count=100;//TODO - t->header = CreateWindowExW(0, - WC_HEADERW, L"", - // TODO is HOTTRACK needed? - WS_CHILD | HDS_FULLDRAG | HDS_HORZ | HDS_HOTTRACK, - 0, 0, 0, 0, - t->hwnd, (HMENU) 100, cs->hInstance, NULL); - if (t->header == NULL) - abort(); -{t->imagelist = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32, 1, 1); -if(t->imagelist==NULL)abort(); -{ -HICON icon; -int unused; -icon = LoadIconW(NULL, IDI_ERROR); -if(icon == NULL)abort(); -if (ImageList_AddIcon(t->imagelist, icon) == -1)abort(); -if (ImageList_GetIconSize(t->imagelist, &unused, &(t->imagelistHeight)) == 0)abort(); -} -} - t->checkboxes = makeCheckboxImageList(t->hwnd, &(t->theme), &(t->checkboxWidth), &(t->checkboxHeight)); - t->focusedColumn = -1; -//TODO retrack(t); - SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) t); - } - // even if we did the above, fall through - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - } - for (hf = handlerfuncs; *hf != NULL; hf++) - if ((*hf)(t, uMsg, wParam, lParam, &lResult)) - return lResult; - switch (uMsg) { - case WM_PAINT: - dc = BeginPaint(hwnd, &ps); - if (dc == NULL) - abort(); - drawItems(t, dc, ps.rcPaint); - EndPaint(hwnd, &ps); - return 0; - case WM_SIZE: - resize(t); - return 0; - case WM_LBUTTONDOWN: - selectItem(t, wParam, lParam); - return 0; - case WM_SETFOCUS: - case WM_KILLFOCUS: - // all we need to do here is redraw the highlight - // TODO ensure giving focus works right - // TODO figure out hwat I meant by this - redrawRow(t, t->selected); - return 0; - case WM_KEYDOWN: - keySelect(t, wParam, lParam); - return 0; - // TODO header double-click - case WM_NOTIFY: - if (nmhdr->hwndFrom == t->header) - switch (nmhdr->code) { - // I could use HDN_TRACK but wine doesn't emit that - case HDN_ITEMCHANGING: - case HDN_ITEMCHANGED: // TODO needed? - recomputeHScroll(t); - redrawAll(t); - return FALSE; - } - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - // TODO others? - case WM_WININICHANGE: - case WM_SYSCOLORCHANGE: - case WM_THEMECHANGED: - if (ImageList_Destroy(t->checkboxes) == 0) - abort(); - t->checkboxes = makeCheckboxImageList(t->hwnd, &(t->theme), &(t->checkboxWidth), &(t->checkboxHeight)); - resize(t); // TODO needed? - redrawAll(t); - // now defer back to DefWindowProc() in case other things are needed - // TODO needed? - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - case WM_GETOBJECT: // accessibility -/* - if (((DWORD) lParam) == OBJID_CLIENT) { - TODO *server; - LRESULT lResult; - - // TODO create the server object - lResult = LresultFromObject(IID_IAccessible, wParam, server); - if (/* TODO failure *|/) - abort(); - // TODO release object - return lResult; - } -*/ - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - default: - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - } - abort(); - return 0; // unreached -} - -void makeTableWindowClass(void) -{ - WNDCLASSW wc; - - ZeroMemory(&wc, sizeof (WNDCLASSW)); - wc.lpszClassName = tableWindowClass; - wc.lpfnWndProc = tableWndProc; - wc.hCursor = LoadCursorW(NULL, IDC_ARROW); - wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); - wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // TODO correct? - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.hInstance = GetModuleHandle(NULL); - if (RegisterClassW(&wc) == 0) - abort(); -} - -int main(int argc, char *argv[]) -{ - HWND mainwin; - MSG msg; - INITCOMMONCONTROLSEX icc; - - ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX)); - icc.dwSize = sizeof (INITCOMMONCONTROLSEX); - icc.dwICC = ICC_LISTVIEW_CLASSES; - if (InitCommonControlsEx(&icc) == 0) - abort(); - makeTableWindowClass(); - mainwin = CreateWindowExW(0, - tableWindowClass, L"Main Window", - WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, - CW_USEDEFAULT, CW_USEDEFAULT, - 400, 400, - NULL, NULL, GetModuleHandle(NULL), NULL); - if (mainwin == NULL) - abort(); - SendMessageW(mainwin, tableAddColumn, tableColumnText, (LPARAM) L"Column"); - SendMessageW(mainwin, tableAddColumn, tableColumnImage, (LPARAM) L"Column 2"); - SendMessageW(mainwin, tableAddColumn, tableColumnCheckbox, (LPARAM) L"Column 3"); - if (argc > 1) { - NONCLIENTMETRICSW ncm; - HFONT font; - - ZeroMemory(&ncm, sizeof (NONCLIENTMETRICSW)); - ncm.cbSize = sizeof (NONCLIENTMETRICSW); - if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof (NONCLIENTMETRICSW), &ncm, sizeof (NONCLIENTMETRICSW)) == 0) - abort(); - font = CreateFontIndirectW(&ncm.lfMessageFont); - if (font == NULL) - abort(); - SendMessageW(mainwin, WM_SETFONT, (WPARAM) font, TRUE); - } - ShowWindow(mainwin, SW_SHOWDEFAULT); - if (UpdateWindow(mainwin) == 0) - abort(); - while (GetMessageW(&msg, NULL, 0, 0) > 0) { - TranslateMessage(&msg); - DispatchMessageW(&msg); - } - return 0; -} diff --git a/wintable/selection.h b/wintable/selection.h deleted file mode 100644 index 12c30d5..0000000 --- a/wintable/selection.h +++ /dev/null @@ -1,83 +0,0 @@ -// 30 november 2014 - -static void finishSelect(struct table *t, intptr_t prev) -{ - if (t->selected < 0) - t->selected = 0; - if (t->selected >= t->count) - t->selected = t->count - 1; - - // always redraw the old and new rows to avoid artifacts when scrolling, even if they are the same (since the focused column may have changed) - redrawRow(t, prev); - if (prev != t->selected) - redrawRow(t, t->selected); - - // if we need to scroll, the scrolling will force a redraw, so we don't have to worry about doing so ourselves - if (t->selected < t->firstVisible) - vscrollto(t, t->selected); - // note that this is not lastVisible(t) because the last visible row may only be partially visible and we want selections to make them fully visible - else if (t->selected >= (t->firstVisible + t->pagesize)) - vscrollto(t, t->selected - t->pagesize + 1); -} - -// TODO isolate functionality so other keyboard event handlers can run -static void keySelect(struct table *t, WPARAM wParam, LPARAM lParam) -{ - intptr_t prev; - - // TODO figure out correct behavior with nothing selected - if (t->count == 0) // don't try to do anything if there's nothing to do - return; - prev = t->selected; - switch (wParam) { - case VK_UP: - t->selected--; - break; - case VK_DOWN: - t->selected++; - break; - case VK_PRIOR: - t->selected -= t->pagesize; - break; - case VK_NEXT: - t->selected += t->pagesize; - break; - case VK_HOME: - t->selected = 0; - break; - case VK_END: - t->selected = t->count - 1; - break; - case VK_LEFT: - t->focusedColumn--; - if (t->focusedColumn < 0) - if (t->nColumns == 0) // peg at -1 - t->focusedColumn = -1; - else - t->focusedColumn = 0; - break; - case VK_RIGHT: - t->focusedColumn++; - if (t->focusedColumn >= t->nColumns) - if (t->nColumns == 0) // peg at -1 - t->focusedColumn = -1; - else - t->focusedColumn = t->nColumns - 1; - break; - // TODO keyboard shortcuts for going to the first/last column? - default: - // don't touch anything - return; - } - finishSelect(t, prev); -} - -// TODO rename -static void selectItem(struct table *t, WPARAM wParam, LPARAM lParam) -{ - intptr_t prev; - - prev = t->selected; - lParamToRowColumn(t, lParam, &(t->selected), &(t->focusedColumn)); - finishSelect(t, prev); -} diff --git a/wintable/util.h b/wintable/util.h deleted file mode 100644 index c12435f..0000000 --- a/wintable/util.h +++ /dev/null @@ -1,136 +0,0 @@ -// 29 november 2014 - -static LONG rowHeight(struct table *t) -{ - HFONT thisfont, prevfont; - TEXTMETRICW tm; - HDC dc; - LONG ret; - - dc = GetDC(t->hwnd); - if (dc == NULL) - abort(); - thisfont = t->font; // in case WM_SETFONT happens before we return - prevfont = (HFONT) SelectObject(dc, thisfont); - if (prevfont == NULL) - abort(); - if (GetTextMetricsW(dc, &tm) == 0) - abort(); - if (SelectObject(dc, prevfont) != (HGDIOBJ) (thisfont)) - abort(); - if (ReleaseDC(t->hwnd, dc) == 0) - abort(); - ret = tm.tmHeight; - if (ret < t->imagelistHeight) - ret = t->imagelistHeight; - if (ret < t->checkboxHeight) - ret = t->checkboxHeight; - return ret; -} - -static void redrawAll(struct table *t) -{ - if (InvalidateRect(t->hwnd, NULL, TRUE) == 0) - abort(); - if (UpdateWindow(t->hwnd) == 0) - abort(); -} - -static RECT realClientRect(struct table *t) -{ - RECT r; - - if (GetClientRect(t->hwnd, &r) == 0) - abort(); - r.top += t->headerHeight; - return r; -} - -static void repositionHeader(struct table *t) -{ - RECT r; - HDLAYOUT headerlayout; - WINDOWPOS headerpos; - - if (GetClientRect(t->hwnd, &r) == 0) // use the whole client rect - abort(); - // grow the rectangle to the left to fake scrolling - r.left -= t->hpos; - headerlayout.prc = &r; - headerlayout.pwpos = &headerpos; - if (SendMessageW(t->header, HDM_LAYOUT, 0, (LPARAM) (&headerlayout)) == FALSE) - abort(); - if (SetWindowPos(t->header, headerpos.hwndInsertAfter, headerpos.x, headerpos.y, headerpos.cx, headerpos.cy, headerpos.flags | SWP_SHOWWINDOW) == 0) - abort(); - t->headerHeight = headerpos.cy; -} - -// this counts partially visible rows -// for all fully visible rows use t->pagesize -// cliprect and rowHeight must be specified here to avoid recomputing things multiple times -static intptr_t lastVisible(struct table *t, RECT cliprect, LONG rowHeight) -{ - intptr_t last; - - last = ((cliprect.bottom + rowHeight - 1) / rowHeight) + t->firstVisible; - if (last >= t->count) - last = t->count; - return last; -} - -static void redrawRow(struct table *t, intptr_t row) -{ - RECT r; - intptr_t height; - - r = realClientRect(t); - height = rowHeight(t); - if (row < t->firstVisible || row > lastVisible(t, r, height)) // not visible; don't bother - return; - r.top = (row - t->firstVisible) * height + t->headerHeight; - r.bottom = r.top + height; - // keep the width and height the same; it spans the client area anyway - if (InvalidateRect(t->hwnd, &r, TRUE) == 0) - abort(); - if (UpdateWindow(t->hwnd) == 0) - abort(); -} - -static intptr_t hitTestColumn(struct table *t, int x) -{ - HDITEMW item; - intptr_t i; - - // TODO count dividers - for (i = 0; i < t->nColumns; i++) { - ZeroMemory(&item, sizeof (HDITEMW)); - item.mask = HDI_WIDTH; - if (SendMessageW(t->header, HDM_GETITEM, (WPARAM) i, (LPARAM) (&item)) == FALSE) - abort(); - if (x < item.cxy) - return i; - x -= item.cxy; // not yet - } - // no column - return -1; -} - -static void lParamToRowColumn(struct table *t, LPARAM lParam, intptr_t *row, intptr_t *column) -{ - int x, y; - LONG h; - - x = GET_X_LPARAM(lParam); - y = GET_Y_LPARAM(lParam); - h = rowHeight(t); - y += t->firstVisible * h; - y -= t->headerHeight; - y /= h; - if (row != NULL) { - *row = y; - if (*row >= t->count) - *row = -1; - } - if (column != NULL) - *column = hitTestColumn(t, x); -} diff --git a/wintable/vscroll.h b/wintable/vscroll.h deleted file mode 100644 index 326d853..0000000 --- a/wintable/vscroll.h +++ /dev/null @@ -1,112 +0,0 @@ -// 29 november 2014 - -static void vscrollto(struct table *t, intptr_t newpos) -{ - SCROLLINFO si; - RECT scrollArea; - - if (newpos < 0) - newpos = 0; - if (newpos > (t->count - t->pagesize)) - newpos = (t->count - t->pagesize); - - scrollArea = realClientRect(t); - - // negative because ScrollWindowEx() is "backwards" - if (ScrollWindowEx(t->hwnd, 0, (-(newpos - t->firstVisible)) * rowHeight(t), - &scrollArea, &scrollArea, NULL, NULL, - SW_ERASE | SW_INVALIDATE) == ERROR) - abort(); - t->firstVisible = newpos; - - ZeroMemory(&si, sizeof (SCROLLINFO)); - si.cbSize = sizeof (SCROLLINFO); - si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; - si.nPage = t->pagesize; - si.nMin = 0; - si.nMax = t->count - 1; // nMax is inclusive - si.nPos = t->firstVisible; - SetScrollInfo(t->hwnd, SB_VERT, &si, TRUE); -} - -static void vscrollby(struct table *t, intptr_t n) -{ - vscrollto(t, t->firstVisible + n); -} - -static void wheelscroll(struct table *t, WPARAM wParam) -{ - int delta; - int lines; - UINT scrollAmount; - - delta = GET_WHEEL_DELTA_WPARAM(wParam); - if (SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &scrollAmount, 0) == 0) - abort(); - if (scrollAmount == WHEEL_PAGESCROLL) - scrollAmount = t->pagesize; - if (scrollAmount == 0) // no mouse wheel scrolling (or t->pagesize == 0) - return; - // the rest of this is basically http://blogs.msdn.com/b/oldnewthing/archive/2003/08/07/54615.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/08/11/54624.aspx - // see those pages for information on subtleties - delta += t->wheelCarry; - lines = delta * ((int) scrollAmount) / WHEEL_DELTA; - t->wheelCarry = delta - lines * WHEEL_DELTA / ((int) scrollAmount); - vscrollby(t, -lines); -} - -static void vscroll(struct table *t, WPARAM wParam) -{ - SCROLLINFO si; - intptr_t newpos; - - ZeroMemory(&si, sizeof (SCROLLINFO)); - si.cbSize = sizeof (SCROLLINFO); - si.fMask = SIF_POS | SIF_TRACKPOS; - if (GetScrollInfo(t->hwnd, SB_VERT, &si) == 0) - abort(); - - newpos = t->firstVisible; - switch (LOWORD(wParam)) { - case SB_TOP: - newpos = 0; - break; - case SB_BOTTOM: - newpos = t->count - t->pagesize; - break; - case SB_LINEUP: - newpos--; - break; - case SB_LINEDOWN: - newpos++; - break; - case SB_PAGEUP: - newpos -= t->pagesize; - break; - case SB_PAGEDOWN: - newpos += t->pagesize; - break; - case SB_THUMBPOSITION: - newpos = (intptr_t) (si.nPos); - break; - case SB_THUMBTRACK: - newpos = (intptr_t) (si.nTrackPos); - } - - vscrollto(t, newpos); -} - -HANDLER(vscroll) -{ - switch (uMsg) { - case WM_VSCROLL: - vscroll(t, wParam); - *lResult = 0; - return TRUE; - case WM_MOUSEWHEEL: - wheelscroll(t, wParam); - *lResult = 0; - return TRUE; - } - return FALSE; -}