// 8 december 2014 static void addColumn(struct table *t, WPARAM wParam, LPARAM lParam) { t->nColumns++; t->columnTypes = (int *) tableRealloc(t->columnTypes, t->nColumns * sizeof (int), "adding the new column type to the current Table's list of column types"); t->columnTypes[t->nColumns - 1] = (int) wParam; // TODO make a panicNoErrCode() or panicArg() for this if (t->columnTypes[t->nColumns - 1] >= nTableColumnTypes) panic("invalid column type passed to tableAddColumn"); headerAddColumn(t, (WCHAR *) lParam); update(t, TRUE); // TODO only redraw the part of the client area where the new client went, if any // (TODO when — if — adding autoresize, figure this one out) // TODO send a notification for all rows? } // TODO what happens if the currently selected row is lost? static void setRowCount(struct table *t, intptr_t rc) { intptr_t old, i; old = t->count; t->count = rc; // we DO redraw everything because we don't want any rows that should no longer be there to remain on screen! updateAll(t); // DONE // TODO reset checkbox and selection logic if the current row for both no longer exists // TODO really send all these notifications? if (old < t->count) // rows added for (i = old; i < t->count; i++) NotifyWinEvent(EVENT_OBJECT_CREATE, t->hwnd, OBJID_CLIENT, i); else if (old > t->count) // rows removed for (i = old; i > t->count; i++) NotifyWinEvent(EVENT_OBJECT_DESTROY, t->hwnd, OBJID_CLIENT, i); // TODO update existing rows? } HANDLER(apiHandlers) { intptr_t *rcp; intptr_t row; switch (uMsg) { case WM_SETFONT: // don't free the old font; see http://blogs.msdn.com/b/oldnewthing/archive/2008/09/12/8945692.aspx t->font = (HFONT) wParam; SendMessageW(t->header, WM_SETFONT, wParam, lParam); // if we redraw, we have to redraw ALL of it; after all, the font changed! if (LOWORD(lParam) != FALSE) updateAll(t); // DONE else update(t, FALSE); // DONE *lResult = 0; return TRUE; case WM_GETFONT: *lResult = (LRESULT) (t->font); return TRUE; case tableAddColumn: addColumn(t, wParam, lParam); *lResult = 0; return TRUE; case tableSetRowCount: rcp = (intptr_t *) lParam; setRowCount(t, *rcp); *lResult = 0; return TRUE; case tableGetSelection: rcp = (intptr_t *) wParam; if (rcp != NULL) *rcp = t->selectedRow; rcp = (intptr_t *) lParam; if (rcp != NULL) *rcp = t->selectedColumn; *lResult = 0; return TRUE; case tableSetSelection: // TODO does doselect() do validation? rcp = (intptr_t *) wParam; row = *rcp; rcp = (intptr_t *) lParam; if (rcp == NULL) if (row == -1) doselect(t, -1, -1); else // select column 0, just like keyboard selections; TODO what if there aren't any columns? doselect(t, row, 0); else doselect(t, row, *rcp); *lResult = 0; return TRUE; } return FALSE; } static LRESULT notify(struct table *t, UINT code, intptr_t row, intptr_t column, uintptr_t data) { tableNM nm; ZeroMemory(&nm, sizeof (tableNM)); nm.nmhdr.hwndFrom = t->hwnd; // TODO check for error from here? 0 is a valid ID (IDCANCEL) nm.nmhdr.idFrom = GetDlgCtrlID(t->hwnd); nm.nmhdr.code = code; nm.row = row; nm.column = column; nm.columnType = t->columnTypes[nm.column]; nm.data = data; // TODO check for error from GetParent()? return SendMessageW(GetParent(t->hwnd), WM_NOTIFY, (WPARAM) (nm.nmhdr.idFrom), (LPARAM) (&nm)); }