2018-05-29 19:26:48 -05:00
|
|
|
#include "uipriv_windows.hpp"
|
2018-06-09 23:49:44 -05:00
|
|
|
#include "table.hpp"
|
2018-05-29 19:26:48 -05:00
|
|
|
|
2018-06-08 20:44:55 -05:00
|
|
|
static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = {
|
2018-06-08 21:05:11 -05:00
|
|
|
/*TODO.ColorModelColumn = */-1,
|
2018-06-08 20:44:55 -05:00
|
|
|
};
|
|
|
|
|
2018-05-29 19:26:48 -05:00
|
|
|
uiTableModel *uiNewTableModel(uiTableModelHandler *mh)
|
|
|
|
{
|
|
|
|
uiTableModel *m;
|
|
|
|
|
2018-06-07 21:54:01 -05:00
|
|
|
m = uiprivNew(uiTableModel);
|
2018-05-29 19:26:48 -05:00
|
|
|
m->mh = mh;
|
2018-06-07 21:54:01 -05:00
|
|
|
m->tables = new std::vector<uiTable *>;
|
2018-05-29 19:26:48 -05:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiFreeTableModel(uiTableModel *m)
|
|
|
|
{
|
2018-06-07 21:54:01 -05:00
|
|
|
delete m->tables;
|
|
|
|
uiprivFree(m);
|
2018-05-29 19:26:48 -05:00
|
|
|
}
|
|
|
|
|
2018-06-08 00:35:23 -05:00
|
|
|
// TODO document that when this is called, the model must return the new row count when asked
|
2018-05-29 19:26:48 -05:00
|
|
|
void uiTableModelRowInserted(uiTableModel *m, int newIndex)
|
|
|
|
{
|
|
|
|
LVITEMW item;
|
2018-06-08 00:35:23 -05:00
|
|
|
int newCount;
|
2018-05-29 19:26:48 -05:00
|
|
|
|
2018-06-08 00:35:23 -05:00
|
|
|
newCount = (*(m->mh->NumRows))(m->mh, m);
|
2018-05-29 19:26:48 -05:00
|
|
|
ZeroMemory(&item, sizeof (LVITEMW));
|
|
|
|
item.mask = 0;
|
|
|
|
item.iItem = newIndex;
|
|
|
|
item.iSubItem = 0;
|
2018-06-08 00:35:23 -05:00
|
|
|
for (auto t : *(m->tables)) {
|
|
|
|
// actually insert the rows
|
|
|
|
if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) newCount, LVSICF_NOINVALIDATEALL) == 0)
|
|
|
|
logLastError(L"error calling LVM_SETITEMCOUNT in uiTableModelRowInserted()");
|
|
|
|
// and redraw every row from the new row down to simulate adding it
|
|
|
|
if (SendMessageW(t->hwnd, LVM_REDRAWITEMS, (WPARAM) newIndex, (LPARAM) (newCount - 1)) == FALSE)
|
|
|
|
logLastError(L"error calling LVM_REDRAWITEMS in uiTableModelRowInserted()");
|
|
|
|
|
|
|
|
// update selection state
|
2018-05-29 19:26:48 -05:00
|
|
|
if (SendMessageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1))
|
2018-06-08 00:35:23 -05:00
|
|
|
logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted() to update selection state");
|
|
|
|
}
|
2018-05-29 19:26:48 -05:00
|
|
|
}
|
|
|
|
|
2018-06-08 00:35:23 -05:00
|
|
|
// TODO compare LVM_UPDATE and LVM_REDRAWITEMS
|
2018-05-29 19:26:48 -05:00
|
|
|
void uiTableModelRowChanged(uiTableModel *m, int index)
|
|
|
|
{
|
2018-06-07 21:54:01 -05:00
|
|
|
for (auto t : *(m->tables))
|
2018-05-29 19:26:48 -05:00
|
|
|
if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) index, 0) == (LRESULT) (-1))
|
|
|
|
logLastError(L"error calling LVM_UPDATE in uiTableModelRowChanged()");
|
|
|
|
}
|
|
|
|
|
2018-06-08 00:35:23 -05:00
|
|
|
// TODO document that when this is called, the model must return the OLD row count when asked
|
|
|
|
// TODO for this and the above, see what GTK+ requires and adjust accordingly
|
2018-05-29 19:26:48 -05:00
|
|
|
void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
|
|
|
|
{
|
2018-06-08 00:35:23 -05:00
|
|
|
int newCount;
|
|
|
|
|
|
|
|
newCount = (*(m->mh->NumRows))(m->mh, m);
|
|
|
|
newCount--;
|
|
|
|
for (auto t : *(m->tables)) {
|
|
|
|
// update selection state
|
2018-05-29 19:26:48 -05:00
|
|
|
if (SendMessageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (-1))
|
2018-06-08 00:35:23 -05:00
|
|
|
logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted() to update selection state");
|
|
|
|
|
|
|
|
// actually delete the rows
|
|
|
|
if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) newCount, LVSICF_NOINVALIDATEALL) == 0)
|
|
|
|
logLastError(L"error calling LVM_SETITEMCOUNT in uiTableModelRowDeleted()");
|
|
|
|
// and redraw every row from the new nth row down to simulate removing the old nth row
|
|
|
|
if (SendMessageW(t->hwnd, LVM_REDRAWITEMS, (WPARAM) oldIndex, (LPARAM) (newCount - 1)) == FALSE)
|
|
|
|
logLastError(L"error calling LVM_REDRAWITEMS in uiTableModelRowDeleted()");
|
|
|
|
}
|
2018-05-29 19:26:48 -05:00
|
|
|
}
|
|
|
|
|
2018-06-08 20:16:06 -05:00
|
|
|
static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm)
|
2018-05-29 19:26:48 -05:00
|
|
|
{
|
2018-06-09 23:49:44 -05:00
|
|
|
static uiprivTableColumnParams *p;
|
2018-06-08 20:16:06 -05:00
|
|
|
uiTableData *data;
|
|
|
|
WCHAR *wstr;
|
2018-06-09 19:35:05 -05:00
|
|
|
HDC dc;
|
|
|
|
IWICBitmap *wb;
|
|
|
|
HBITMAP b;
|
2018-06-09 22:02:37 -05:00
|
|
|
int checked;
|
2018-06-08 21:11:46 -05:00
|
|
|
bool queueUpdated = false;
|
2018-06-08 20:16:06 -05:00
|
|
|
|
2018-06-08 21:05:11 -05:00
|
|
|
wstr = t->dispinfoStrings->front();
|
2018-06-08 20:44:55 -05:00
|
|
|
if (wstr != NULL)
|
|
|
|
uiprivFree(wstr);
|
2018-06-08 21:05:11 -05:00
|
|
|
t->dispinfoStrings->pop();
|
2018-06-08 20:16:06 -05:00
|
|
|
|
|
|
|
p = (*(t->columns))[nm->item.iSubItem];
|
2018-06-09 22:02:37 -05:00
|
|
|
nm->item.pszText=L"abcdefg";
|
2018-06-08 20:16:06 -05:00
|
|
|
if ((nm->item.mask & LVIF_TEXT) != 0)
|
|
|
|
if (p->textModelColumn != -1) {
|
2018-06-08 21:05:11 -05:00
|
|
|
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn);
|
2018-06-08 20:16:06 -05:00
|
|
|
wstr = toUTF16(uiTableDataString(data));
|
|
|
|
uiFreeTableData(data);
|
|
|
|
nm->item.pszText = wstr;
|
2018-06-08 21:05:11 -05:00
|
|
|
t->dispinfoStrings->push(wstr);
|
2018-06-08 21:11:46 -05:00
|
|
|
queueUpdated = true;
|
2018-06-08 20:16:06 -05:00
|
|
|
}
|
2018-05-29 19:26:48 -05:00
|
|
|
|
2018-06-09 23:49:44 -05:00
|
|
|
hr = uiprivLVN_GETDISPINFOImagesCheckboxes(t, nm, p);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
// TODO
|
2018-06-09 22:25:16 -05:00
|
|
|
}
|
2018-06-09 22:02:37 -05:00
|
|
|
|
2018-06-08 21:11:46 -05:00
|
|
|
// we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL
|
|
|
|
if (!queueUpdated)
|
|
|
|
t->dispinfoStrings->push(NULL);
|
2018-06-08 20:16:06 -05:00
|
|
|
return 0;
|
2018-05-29 19:26:48 -05:00
|
|
|
}
|
|
|
|
|
2018-06-09 09:40:42 -05:00
|
|
|
static COLORREF blend(COLORREF base, double r, double g, double b, double a)
|
|
|
|
{
|
|
|
|
double br, bg, bb;
|
|
|
|
|
2018-06-09 11:15:50 -05:00
|
|
|
// TODO find a better fix than this
|
|
|
|
// TODO s listview already alphablending?
|
|
|
|
// TODO find the right color here
|
2018-06-09 12:57:43 -05:00
|
|
|
if (base == CLR_DEFAULT)
|
2018-06-09 11:15:50 -05:00
|
|
|
base = GetSysColor(COLOR_WINDOW);
|
|
|
|
br = ((double) GetRValue(base)) / 255.0;
|
|
|
|
bg = ((double) GetGValue(base)) / 255.0;
|
|
|
|
bb = ((double) GetBValue(base)) / 255.0;
|
2018-06-09 09:40:42 -05:00
|
|
|
|
|
|
|
br = (r * a) + (br * (1.0 - a));
|
|
|
|
bg = (g * a) + (bg * (1.0 - a));
|
|
|
|
bb = (b * a) + (bb * (1.0 - a));
|
|
|
|
return RGB((BYTE) (br * 255),
|
|
|
|
(BYTE) (bg * 255),
|
|
|
|
(BYTE) (bb * 255));
|
|
|
|
}
|
|
|
|
|
|
|
|
static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm)
|
|
|
|
{
|
2018-06-09 23:49:44 -05:00
|
|
|
uiprivTableColumnParams *p;
|
2018-06-09 09:40:42 -05:00
|
|
|
uiTableData *data;
|
|
|
|
double r, g, b, a;
|
|
|
|
LRESULT ret;
|
|
|
|
|
|
|
|
switch (nm->nmcd.dwDrawStage) {
|
|
|
|
case CDDS_PREPAINT:
|
|
|
|
return CDRF_NOTIFYITEMDRAW;
|
|
|
|
case CDDS_ITEMPREPAINT:
|
|
|
|
if (t->backgroundColumn != -1) {
|
|
|
|
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, t->backgroundColumn);
|
|
|
|
if (data != NULL) {
|
|
|
|
uiTableDataColor(data, &r, &g, &b, &a);
|
|
|
|
uiFreeTableData(data);
|
|
|
|
nm->clrTextBk = blend(nm->clrTextBk, r, g, b, a);
|
|
|
|
}
|
|
|
|
}
|
2018-06-09 11:15:50 -05:00
|
|
|
t->clrItemText = nm->clrText;
|
2018-06-09 09:40:42 -05:00
|
|
|
return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW;
|
|
|
|
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
|
|
|
|
p = (*(t->columns))[nm->iSubItem];
|
2018-06-09 11:15:50 -05:00
|
|
|
// we need this as previous subitems will persist their colors
|
|
|
|
nm->clrText = t->clrItemText;
|
2018-06-09 09:40:42 -05:00
|
|
|
if (p->textParams.ColorModelColumn != -1) {
|
|
|
|
data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textParams.ColorModelColumn);
|
|
|
|
if (data != NULL) {
|
|
|
|
uiTableDataColor(data, &r, &g, &b, &a);
|
|
|
|
uiFreeTableData(data);
|
2018-06-09 11:15:50 -05:00
|
|
|
nm->clrText = blend(nm->clrTextBk, r, g, b, a);
|
2018-06-09 09:40:42 -05:00
|
|
|
}
|
|
|
|
}
|
2018-06-09 12:57:43 -05:00
|
|
|
// TODO draw background on image columns if needed
|
2018-06-09 09:40:42 -05:00
|
|
|
return CDRF_NEWFONT;
|
|
|
|
}
|
|
|
|
return CDRF_DODEFAULT;
|
|
|
|
}
|
|
|
|
|
2018-06-08 20:16:06 -05:00
|
|
|
static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
|
2018-05-29 19:26:48 -05:00
|
|
|
{
|
2018-06-08 20:16:06 -05:00
|
|
|
uiTable *t = uiTable(c);
|
2018-05-29 19:26:48 -05:00
|
|
|
|
2018-06-08 20:16:06 -05:00
|
|
|
switch (nmhdr->code) {
|
|
|
|
case LVN_GETDISPINFO:
|
|
|
|
*lResult = onLVN_GETDISPINFO(t, (NMLVDISPINFOW *) nmhdr);
|
|
|
|
return TRUE;
|
2018-06-09 09:40:42 -05:00
|
|
|
case NM_CUSTOMDRAW:
|
|
|
|
*lResult = onNM_CUSTOMDRAW(t, (NMLVCUSTOMDRAW *) nmhdr);
|
|
|
|
return TRUE;
|
2018-06-08 20:16:06 -05:00
|
|
|
}
|
|
|
|
return FALSE;
|
2018-05-29 19:26:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void uiTableDestroy(uiControl *c)
|
|
|
|
{
|
|
|
|
uiTable *t = uiTable(c);
|
|
|
|
uiTableModel *model = t->model;
|
|
|
|
std::vector<uiTable *>::iterator it;
|
2018-06-08 20:44:55 -05:00
|
|
|
WCHAR *wstr;
|
2018-05-29 19:26:48 -05:00
|
|
|
|
|
|
|
uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd);
|
|
|
|
uiWindowsEnsureDestroyWindow(t->hwnd);
|
|
|
|
// detach table from model
|
2018-06-08 21:05:11 -05:00
|
|
|
for (it = model->tables->begin(); it != model->tables->end(); it++) {
|
2018-05-29 19:26:48 -05:00
|
|
|
if (*it == t) {
|
2018-06-08 20:44:55 -05:00
|
|
|
model->tables->erase(it);
|
2018-05-29 19:26:48 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-06-08 20:44:55 -05:00
|
|
|
// empty the string queue
|
|
|
|
while (t->dispinfoStrings->size() != 0) {
|
|
|
|
wstr = t->dispinfoStrings->front();
|
|
|
|
if (wstr != NULL)
|
|
|
|
uiprivFree(wstr);
|
|
|
|
t->dispinfoStrings->pop();
|
|
|
|
}
|
|
|
|
delete t->dispinfoStrings;
|
2018-05-29 19:26:48 -05:00
|
|
|
// free the columns
|
2018-06-08 20:44:55 -05:00
|
|
|
for (auto col : *(t->columns))
|
2018-05-29 19:26:48 -05:00
|
|
|
uiprivFree(col);
|
2018-06-07 21:54:01 -05:00
|
|
|
delete t->columns;
|
2018-06-09 19:35:05 -05:00
|
|
|
// t->smallImages will be automatically destroyed
|
2018-05-29 19:26:48 -05:00
|
|
|
uiFreeControl(uiControl(t));
|
|
|
|
}
|
|
|
|
|
2018-06-08 20:16:06 -05:00
|
|
|
uiWindowsControlAllDefaultsExceptDestroy(uiTable)
|
|
|
|
|
2018-05-29 19:26:48 -05:00
|
|
|
// suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing:
|
|
|
|
// "columns widths that avoid truncated data x an integral number of items"
|
|
|
|
// Don't think that'll cut it when some cells have overlong data (eg
|
|
|
|
// stupidly long URLs). So for now, just hardcode a minimum.
|
|
|
|
// TODO: Investigate using LVM_GETHEADER/HDM_LAYOUT here...
|
|
|
|
#define tableMinWidth 107 /* in line with other controls */
|
2018-06-08 20:44:55 -05:00
|
|
|
#define tableMinHeight (14 * 3) /* header + 2 lines (roughly) */
|
2018-05-29 19:26:48 -05:00
|
|
|
|
|
|
|
static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height)
|
|
|
|
{
|
|
|
|
uiTable *t = uiTable(c);
|
|
|
|
uiWindowsSizing sizing;
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
x = tableMinWidth;
|
|
|
|
y = tableMinHeight;
|
|
|
|
uiWindowsGetSizing(t->hwnd, &sizing);
|
|
|
|
uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y);
|
|
|
|
*width = x;
|
|
|
|
*height = y;
|
2018-06-08 20:45:30 -05:00
|
|
|
}
|
2018-05-29 19:26:48 -05:00
|
|
|
|
2018-06-09 23:49:44 -05:00
|
|
|
static uiprivTableColumnParams *appendColumn(uiTable *t, const char *name, int colfmt)
|
2018-05-29 19:26:48 -05:00
|
|
|
{
|
2018-06-08 20:44:55 -05:00
|
|
|
WCHAR *wstr;
|
2018-06-08 20:16:06 -05:00
|
|
|
LVCOLUMNW lvc;
|
2018-06-09 23:49:44 -05:00
|
|
|
uiprivTableColumnParams *p;
|
2018-05-29 19:26:48 -05:00
|
|
|
|
2018-06-08 20:44:55 -05:00
|
|
|
ZeroMemory(&lvc, sizeof (LVCOLUMNW));
|
|
|
|
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
|
|
|
|
lvc.fmt = colfmt;
|
|
|
|
lvc.cx = 120; // TODO
|
|
|
|
wstr = toUTF16(name);
|
|
|
|
lvc.pszText = wstr;
|
|
|
|
if (SendMessageW(t->hwnd, LVM_INSERTCOLUMNW, t->nColumns, (LPARAM) (&lvc)) == (LRESULT) (-1))
|
|
|
|
logLastError(L"error calling LVM_INSERTCOLUMNW in appendColumn()");
|
|
|
|
uiprivFree(wstr);
|
|
|
|
t->nColumns++;
|
|
|
|
|
2018-06-09 23:49:44 -05:00
|
|
|
p = uiprivNew(uiprivTableColumnParams);
|
2018-06-08 20:44:55 -05:00
|
|
|
p->textModelColumn = -1;
|
2018-06-09 22:02:37 -05:00
|
|
|
p->textEditableColumn = -1;
|
|
|
|
p->textParams = defaultTextColumnOptionalParams;
|
2018-06-08 20:44:55 -05:00
|
|
|
p->imageModelColumn = -1;
|
|
|
|
p->checkboxModelColumn = -1;
|
2018-06-09 22:02:37 -05:00
|
|
|
p->checkboxEditableColumn = -1;
|
2018-06-08 20:44:55 -05:00
|
|
|
p->progressBarModelColumn = -1;
|
|
|
|
p->buttonModelColumn = -1;
|
|
|
|
t->columns->push_back(p);
|
|
|
|
return p;
|
|
|
|
}
|
2018-06-08 20:16:06 -05:00
|
|
|
|
2018-06-08 20:44:55 -05:00
|
|
|
void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params)
|
|
|
|
{
|
2018-06-09 23:49:44 -05:00
|
|
|
uiprivTableColumnParams *p;
|
2018-06-08 20:16:06 -05:00
|
|
|
|
2018-06-08 20:44:55 -05:00
|
|
|
p = appendColumn(t, name, LVCFMT_LEFT);
|
|
|
|
p->textModelColumn = textModelColumn;
|
|
|
|
p->textEditableColumn = textEditableModelColumn;
|
|
|
|
if (params != NULL)
|
|
|
|
p->textParams = *params;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn)
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
|
|
|
|
{
|
2018-06-09 23:49:44 -05:00
|
|
|
uiprivTableColumnParams *p;
|
2018-06-08 20:44:55 -05:00
|
|
|
|
2018-06-09 11:15:50 -05:00
|
|
|
p = appendColumn(t, name, LVCFMT_LEFT);
|
2018-06-08 20:44:55 -05:00
|
|
|
p->textModelColumn = textModelColumn;
|
|
|
|
p->textEditableColumn = textEditableModelColumn;
|
2018-06-08 21:05:11 -05:00
|
|
|
if (textParams != NULL)
|
|
|
|
p->textParams = *textParams;
|
2018-06-08 20:44:55 -05:00
|
|
|
p->imageModelColumn = imageModelColumn;
|
2018-06-08 20:16:06 -05:00
|
|
|
}
|
|
|
|
|
2018-06-08 20:44:55 -05:00
|
|
|
void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn)
|
2018-06-08 20:16:06 -05:00
|
|
|
{
|
2018-06-09 23:49:44 -05:00
|
|
|
uiprivTableColumnParams *p;
|
2018-06-09 22:02:37 -05:00
|
|
|
|
|
|
|
p = appendColumn(t, name, LVCFMT_LEFT);
|
|
|
|
p->checkboxModelColumn = checkboxModelColumn;
|
|
|
|
p->checkboxEditableColumn = checkboxEditableModelColumn;
|
2018-06-08 20:16:06 -05:00
|
|
|
}
|
|
|
|
|
2018-06-08 20:44:55 -05:00
|
|
|
void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
|
2018-06-08 20:16:06 -05:00
|
|
|
{
|
2018-06-08 20:44:55 -05:00
|
|
|
struct columnParams *p;
|
|
|
|
|
|
|
|
p = appendColumn(t, name, LVCFMT_LEFT);
|
|
|
|
p->textModelColumn = textModelColumn;
|
|
|
|
p->textEditableColumn = textEditableModelColumn;
|
2018-06-08 21:05:11 -05:00
|
|
|
if (textParams != NULL)
|
|
|
|
p->textParams = *textParams;
|
2018-06-08 20:44:55 -05:00
|
|
|
p->checkboxModelColumn = checkboxModelColumn;
|
|
|
|
p->checkboxEditableColumn = checkboxEditableModelColumn;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn)
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn)
|
|
|
|
{
|
|
|
|
// TODO
|
2018-06-08 20:16:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn)
|
|
|
|
{
|
2018-06-09 09:40:42 -05:00
|
|
|
// TODO make the names consistent
|
|
|
|
t->backgroundColumn = modelColumn;
|
|
|
|
// TODO redraw?
|
2018-05-29 19:26:48 -05:00
|
|
|
}
|
|
|
|
|
2018-06-09 22:02:37 -05:00
|
|
|
// see https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485
|
|
|
|
static UINT unthemedStates[] = {
|
|
|
|
0,
|
|
|
|
DFCS_CHECKED,
|
|
|
|
DFCS_INACTIVE,
|
|
|
|
DFCS_CHECKED | DFCS_INACTIVE,
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO call this whenever either theme, system colors (maybe? TODO), or DPI change
|
|
|
|
static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy)
|
|
|
|
{
|
|
|
|
HDC dc;
|
|
|
|
BITMAPINFO bmi;
|
|
|
|
HBITMAP b;
|
|
|
|
VOID *bits;
|
|
|
|
HDC cdc;
|
|
|
|
HBITMAP prevBitmap;
|
|
|
|
RECT r;
|
2018-06-09 22:25:16 -05:00
|
|
|
int i;
|
2018-06-09 22:02:37 -05:00
|
|
|
|
|
|
|
dc = GetDC(t->hwnd);
|
|
|
|
if (dc == NULL)
|
|
|
|
logLastError(L"error calling GetDC() in mkCheckboxesUnthemed()");
|
|
|
|
ZeroMemory(&bmi, sizeof (BITMAPINFO));
|
|
|
|
bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
|
2018-06-09 22:25:16 -05:00
|
|
|
bmi.bmiHeader.biWidth = cx * nCheckboxImages;
|
2018-06-09 22:02:37 -05:00
|
|
|
bmi.bmiHeader.biHeight = cy;
|
|
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
|
|
bmi.bmiHeader.biBitCount = 32;
|
|
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
|
|
b = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS,
|
|
|
|
&bits, NULL, 0);
|
|
|
|
if (b == NULL)
|
|
|
|
logLastError(L"error calling CreateDIBSection() in mkCheckboxesUnthemed()");
|
|
|
|
|
|
|
|
cdc = CreateCompatibleDC(dc);
|
|
|
|
if (cdc == NULL)
|
|
|
|
logLastError(L"error calling CreateCompatibleDC() in mkCheckboxesUnthemed()");
|
|
|
|
// TODO error check
|
|
|
|
prevBitmap = (HBITMAP) SelectObject(cdc, b);
|
|
|
|
|
2018-06-09 22:25:16 -05:00
|
|
|
r.left = 0;
|
2018-06-09 22:02:37 -05:00
|
|
|
r.top = 0;
|
2018-06-09 22:25:16 -05:00
|
|
|
r.right = cx;
|
2018-06-09 22:02:37 -05:00
|
|
|
r.bottom = cy;
|
2018-06-09 22:25:16 -05:00
|
|
|
for (i = 0; i < nCheckboxImages; i++) {
|
2018-06-09 22:02:37 -05:00
|
|
|
if (DrawFrameControl(cdc, &r,
|
|
|
|
DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0)
|
|
|
|
logLastError(L"error calling DrawFrameControl() in mkCheckboxesUnthemed()");
|
|
|
|
r.left += cx;
|
|
|
|
r.right += cx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO error check
|
|
|
|
SelectObject(cdc, prevBitmap);
|
|
|
|
if (DeleteDC(cdc) == 0)
|
|
|
|
logLastError(L"error calling DeleteDC() in mkCheckboxesUnthemed()");
|
|
|
|
if (ReleaseDC(t->hwnd, dc) == 0)
|
|
|
|
logLastError(L"error calling ReleaseDC() in mkCheckboxesUnthemed()");
|
|
|
|
|
2018-06-09 22:25:16 -05:00
|
|
|
if (ImageList_Replace(t->smallImages, 0, b, NULL) == 0)
|
|
|
|
logLastError(L"error calling ImageList_Replace() in mkCheckboxesUnthemed()");
|
2018-06-09 22:02:37 -05:00
|
|
|
|
|
|
|
// TODO error check
|
|
|
|
DeleteObject(b);
|
|
|
|
}
|
|
|
|
|
2018-05-29 19:26:48 -05:00
|
|
|
uiTable *uiNewTable(uiTableModel *model)
|
|
|
|
{
|
|
|
|
uiTable *t;
|
|
|
|
int n;
|
2018-06-09 22:25:16 -05:00
|
|
|
int i;
|
2018-05-29 19:26:48 -05:00
|
|
|
|
|
|
|
uiWindowsNewControl(uiTable, t);
|
2018-06-07 21:54:01 -05:00
|
|
|
|
2018-06-09 23:49:44 -05:00
|
|
|
t->columns = new std::vector<uiprivTableColumnParams *>;
|
2018-05-29 19:26:48 -05:00
|
|
|
t->model = model;
|
|
|
|
t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE,
|
|
|
|
WC_LISTVIEW, L"",
|
|
|
|
LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL,
|
|
|
|
hInstance, NULL,
|
|
|
|
TRUE);
|
2018-06-08 21:05:11 -05:00
|
|
|
model->tables->push_back(t);
|
2018-05-29 19:26:48 -05:00
|
|
|
uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t));
|
|
|
|
|
|
|
|
// TODO: try LVS_EX_AUTOSIZECOLUMNS
|
|
|
|
SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE,
|
2018-06-09 11:15:50 -05:00
|
|
|
(WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES),
|
|
|
|
(LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES));
|
2018-05-29 19:26:48 -05:00
|
|
|
n = (*(model->mh->NumRows))(model->mh, model);
|
|
|
|
if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0)
|
|
|
|
logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()");
|
2018-06-08 21:05:11 -05:00
|
|
|
|
2018-06-09 19:35:05 -05:00
|
|
|
t->backgroundColumn = -1;
|
|
|
|
|
2018-06-08 21:05:11 -05:00
|
|
|
t->dispinfoStrings = new std::queue<WCHAR *>;
|
|
|
|
// this encodes the idea that two LVN_GETDISPINFOs must complete before we can free a string: the first real one is for the fourth call to free
|
2018-06-09 23:49:44 -05:00
|
|
|
for (i = 0; i < uiprivNumLVN_GETDISPINFOSkip; i++)
|
2018-06-09 22:25:16 -05:00
|
|
|
t->dispinfoStrings->push(NULL);
|
2018-06-08 21:05:11 -05:00
|
|
|
|
2018-06-09 12:57:43 -05:00
|
|
|
// TODO update these when the DPI changes
|
2018-06-09 18:24:36 -05:00
|
|
|
// TODO handle errors
|
2018-06-09 19:35:05 -05:00
|
|
|
t->smallImages = ImageList_Create(
|
2018-06-09 12:57:43 -05:00
|
|
|
GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
|
|
|
|
ILC_COLOR32,
|
2018-06-09 23:49:44 -05:00
|
|
|
nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip, nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip);
|
2018-06-09 19:35:05 -05:00
|
|
|
if (t->smallImages == NULL)
|
|
|
|
logLastError(L"error calling ImageList_Create() in uiNewTable()");
|
2018-06-09 12:57:43 -05:00
|
|
|
// TODO will this return NULL here because it's an initial state?
|
2018-06-09 19:35:05 -05:00
|
|
|
SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages));
|
2018-06-09 12:57:43 -05:00
|
|
|
|
2018-06-09 22:02:37 -05:00
|
|
|
mkCheckboxesUnthemed(t, 16, 16);
|
|
|
|
|
2018-05-29 19:26:48 -05:00
|
|
|
return t;
|
|
|
|
}
|