2014-08-14 09:42:10 -05:00
|
|
|
// 28 july 2014
|
2014-07-28 19:52:32 -05:00
|
|
|
|
|
|
|
#include "winapi_windows.h"
|
2014-07-28 23:47:09 -05:00
|
|
|
#include "_cgo_export.h"
|
2014-07-28 19:52:32 -05:00
|
|
|
|
2014-08-14 09:42:10 -05:00
|
|
|
// provided for cgo's benefit
|
2014-08-01 17:25:59 -05:00
|
|
|
LPWSTR xWC_LISTVIEW = WC_LISTVIEW;
|
2014-07-28 19:52:32 -05:00
|
|
|
|
2014-08-17 16:44:33 -05:00
|
|
|
static void handle(HWND hwnd, WPARAM wParam, LPARAM lParam, void (*handler)(void *, int, int), void *data)
|
2014-08-17 16:22:47 -05:00
|
|
|
{
|
|
|
|
LVHITTESTINFO ht;
|
|
|
|
|
|
|
|
// TODO use wParam
|
|
|
|
ZeroMemory(&ht, sizeof (LVHITTESTINFO));
|
|
|
|
ht.pt.x = GET_X_LPARAM(lParam);
|
|
|
|
ht.pt.y = GET_Y_LPARAM(lParam);
|
|
|
|
if (SendMessageW(hwnd, LVM_SUBITEMHITTEST, 0, (LPARAM) (&ht)) == (LRESULT) -1) {
|
2014-08-17 16:44:33 -05:00
|
|
|
(*handler)(data, -1, -1);
|
2014-08-17 16:22:47 -05:00
|
|
|
return; // no item
|
|
|
|
}
|
2014-08-17 16:30:25 -05:00
|
|
|
if (ht.flags != LVHT_ONITEMSTATEICON) {
|
2014-08-17 16:44:33 -05:00
|
|
|
(*handler)(data, -1, -1);
|
|
|
|
return; // not on a checkbox
|
2014-08-17 16:30:25 -05:00
|
|
|
}
|
2014-08-17 16:44:33 -05:00
|
|
|
(*handler)(data, ht.iItem, ht.iSubItem);
|
2014-08-17 16:22:47 -05:00
|
|
|
}
|
|
|
|
|
2014-08-25 01:36:32 -05:00
|
|
|
struct tableData {
|
|
|
|
void *gotable;
|
|
|
|
HTHEME theme;
|
|
|
|
HIMAGELIST checkboxImageList;
|
|
|
|
};
|
|
|
|
|
2014-08-25 10:51:38 -05:00
|
|
|
static void tableSetCheckboxImageList(HWND hwnd, struct tableData *t)
|
|
|
|
{
|
|
|
|
HIMAGELIST checkboxImageList;
|
|
|
|
|
|
|
|
checkboxImageList = makeCheckboxImageList(hwnd);
|
|
|
|
if (SendMessageW(hwnd, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM) (checkboxImageList)) == (LRESULT) NULL)
|
|
|
|
;//TODO xpanic("error setting image list", GetLastError());
|
|
|
|
// TODO free old one here if any/different
|
|
|
|
// thanks to Jonathan Potter (http://stackoverflow.com/questions/25354448/why-do-my-owner-data-list-view-state-images-come-up-as-blank-on-windows-xp)
|
|
|
|
if (SendMessageW(hwnd, LVM_SETCALLBACKMASK, LVIS_STATEIMAGEMASK, 0) == FALSE)
|
|
|
|
xpanic("error marking state image list as application-managed", GetLastError());
|
|
|
|
}
|
|
|
|
|
2014-07-28 19:52:32 -05:00
|
|
|
static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR data)
|
|
|
|
{
|
2014-07-28 22:29:06 -05:00
|
|
|
NMHDR *nmhdr = (NMHDR *) lParam;
|
|
|
|
NMLVDISPINFOW *fill = (NMLVDISPINFO *) lParam;
|
2014-08-20 20:21:45 -05:00
|
|
|
NMLISTVIEW *nlv = (NMLISTVIEW *) lParam;
|
2014-08-25 01:36:32 -05:00
|
|
|
struct tableData *t = (struct tableData *) data;
|
2014-07-28 22:29:06 -05:00
|
|
|
|
2014-07-28 19:52:32 -05:00
|
|
|
switch (uMsg) {
|
|
|
|
case msgNOTIFY:
|
2014-07-28 22:29:06 -05:00
|
|
|
switch (nmhdr->code) {
|
|
|
|
case LVN_GETDISPINFO:
|
2014-08-25 01:36:32 -05:00
|
|
|
tableGetCell(t->gotable, &(fill->item));
|
2014-07-28 22:29:06 -05:00
|
|
|
return 0;
|
2014-08-20 20:21:45 -05:00
|
|
|
case LVN_ITEMCHANGED:
|
|
|
|
if ((nlv->uChanged & LVIF_STATE) == 0)
|
|
|
|
break;
|
|
|
|
// if both old and new states have the same value for the selected bit, then the selection state did not change, regardless of selected or deselected
|
|
|
|
if ((nlv->uOldState & LVIS_SELECTED) == (nlv->uNewState & LVIS_SELECTED))
|
|
|
|
break;
|
2014-08-25 01:36:32 -05:00
|
|
|
tableSelectionChanged(t->gotable);
|
2014-08-20 20:21:45 -05:00
|
|
|
return 0;
|
2014-07-28 22:29:06 -05:00
|
|
|
}
|
2014-07-28 19:52:32 -05:00
|
|
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
2014-08-17 16:22:47 -05:00
|
|
|
case WM_MOUSEMOVE:
|
2014-08-25 01:36:32 -05:00
|
|
|
handle(hwnd, wParam, lParam, tableSetHot, t->gotable);
|
2014-08-17 16:44:33 -05:00
|
|
|
// and let the list view do its thing
|
|
|
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
2014-08-17 17:06:36 -05:00
|
|
|
case WM_LBUTTONDOWN:
|
2014-08-17 18:33:42 -05:00
|
|
|
case WM_LBUTTONDBLCLK: // listviews have CS_DBLCICKS; check this to better mimic the behavior of a real checkbox
|
2014-08-25 01:36:32 -05:00
|
|
|
handle(hwnd, wParam, lParam, tablePushed, t->gotable);
|
2014-08-17 17:06:36 -05:00
|
|
|
// and let the list view do its thing
|
|
|
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
2014-08-17 16:44:33 -05:00
|
|
|
case WM_LBUTTONUP:
|
2014-08-25 01:36:32 -05:00
|
|
|
handle(hwnd, wParam, lParam, tableToggled, t->gotable);
|
2014-08-17 16:22:47 -05:00
|
|
|
// and let the list view do its thing
|
|
|
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
2014-08-17 17:06:36 -05:00
|
|
|
case WM_MOUSELEAVE:
|
|
|
|
// TODO doesn't work
|
2014-08-25 01:36:32 -05:00
|
|
|
tablePushed(t->gotable, -1, -1); // in case button held as drag out
|
2014-08-17 17:06:36 -05:00
|
|
|
// and let the list view do its thing
|
|
|
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
2014-08-25 10:51:38 -05:00
|
|
|
case msgTableMakeInitialImageList:
|
|
|
|
tableSetCheckboxImageList(hwnd, t);
|
|
|
|
return 0;
|
2014-08-14 09:42:10 -05:00
|
|
|
// see table.autoresize() in table_windows.go for the column autosize policy
|
|
|
|
case WM_NOTIFY: // from the contained header control
|
2014-08-06 09:42:26 -05:00
|
|
|
if (nmhdr->code == HDN_BEGINTRACK)
|
2014-08-25 01:36:32 -05:00
|
|
|
tableStopColumnAutosize(t->gotable);
|
2014-08-06 09:42:26 -05:00
|
|
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
2014-07-28 19:52:32 -05:00
|
|
|
case WM_NCDESTROY:
|
2014-08-25 01:36:32 -05:00
|
|
|
free(t);
|
2014-07-28 19:52:32 -05:00
|
|
|
if ((*fv_RemoveWindowSubclass)(hwnd, tableSubProc, id) == FALSE)
|
|
|
|
xpanic("error removing Table subclass (which was for its own event handler)", GetLastError());
|
|
|
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
|
|
|
default:
|
|
|
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
|
|
|
}
|
|
|
|
xmissedmsg("Button", "tableSubProc()", uMsg);
|
2014-08-14 09:42:10 -05:00
|
|
|
return 0; // unreached
|
2014-07-28 19:52:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void setTableSubclass(HWND hwnd, void *data)
|
|
|
|
{
|
2014-08-25 01:36:32 -05:00
|
|
|
struct tableData *t;
|
|
|
|
|
2014-08-25 12:42:29 -05:00
|
|
|
t = (struct tableData *) malloc(sizeof (struct tableData));
|
2014-08-25 01:36:32 -05:00
|
|
|
if (t == NULL)
|
|
|
|
xpanic("error allocating structure for Table extra data", GetLastError());
|
|
|
|
ZeroMemory(t, sizeof (struct tableData));
|
|
|
|
t->gotable = data;
|
|
|
|
if ((*fv_SetWindowSubclass)(hwnd, tableSubProc, 0, (DWORD_PTR) t) == FALSE)
|
2014-07-28 19:52:32 -05:00
|
|
|
xpanic("error subclassing Table to give it its own event handler", GetLastError());
|
|
|
|
}
|
|
|
|
|
2014-08-01 17:25:59 -05:00
|
|
|
void tableAppendColumn(HWND hwnd, int index, LPWSTR name)
|
2014-07-28 19:52:32 -05:00
|
|
|
{
|
|
|
|
LVCOLUMNW col;
|
|
|
|
|
|
|
|
ZeroMemory(&col, sizeof (LVCOLUMNW));
|
|
|
|
col.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
|
|
|
|
col.fmt = LVCFMT_LEFT;
|
|
|
|
col.pszText = name;
|
|
|
|
col.iSubItem = index;
|
|
|
|
col.iOrder = index;
|
|
|
|
if (SendMessageW(hwnd, LVM_INSERTCOLUMN, (WPARAM) index, (LPARAM) (&col)) == (LRESULT) -1)
|
|
|
|
xpanic("error adding column to Table", GetLastError());
|
|
|
|
}
|
|
|
|
|
2014-07-28 22:29:06 -05:00
|
|
|
void tableUpdate(HWND hwnd, int nItems)
|
2014-07-28 19:52:32 -05:00
|
|
|
{
|
2014-07-28 22:29:06 -05:00
|
|
|
if (SendMessageW(hwnd, LVM_SETITEMCOUNT, (WPARAM) nItems, 0) == 0)
|
|
|
|
xpanic("error setting number of items in Table", GetLastError());
|
2014-07-28 19:52:32 -05:00
|
|
|
}
|
2014-07-28 22:47:45 -05:00
|
|
|
|
|
|
|
void tableAddExtendedStyles(HWND hwnd, LPARAM styles)
|
|
|
|
{
|
2014-08-14 09:42:10 -05:00
|
|
|
// the bits of WPARAM specify which bits of LPARAM to look for; having WPARAM == LPARAM ensures that only the bits we want to add are affected
|
2014-07-28 22:47:45 -05:00
|
|
|
SendMessageW(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, (WPARAM) styles, styles);
|
|
|
|
}
|
2014-08-14 08:20:20 -05:00
|
|
|
|
|
|
|
void tableAutosizeColumns(HWND hwnd, int nColumns)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < nColumns; i++)
|
|
|
|
if (SendMessageW(hwnd, LVM_SETCOLUMNWIDTH, (WPARAM) i, (LPARAM) LVSCW_AUTOSIZE_USEHEADER) == FALSE)
|
|
|
|
xpanic("error resizing columns of results list view", GetLastError());
|
|
|
|
}
|
2014-08-17 14:30:10 -05:00
|
|
|
|
2014-08-18 01:22:27 -05:00
|
|
|
// because Go won't let me do C.WPARAM(-1)
|
|
|
|
intptr_t tableSelectedItem(HWND hwnd)
|
|
|
|
{
|
|
|
|
return (intptr_t) SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM) -1, LVNI_SELECTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tableSelectItem(HWND hwnd, intptr_t index)
|
|
|
|
{
|
|
|
|
LVITEMW item;
|
|
|
|
LRESULT current;
|
|
|
|
|
|
|
|
// via http://support.microsoft.com/kb/131284
|
|
|
|
// we don't need to clear the other bits; Tables don't support cutting or drag/drop
|
|
|
|
current = SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM) -1, LVNI_SELECTED);
|
|
|
|
if (current != (LRESULT) -1) {
|
|
|
|
ZeroMemory(&item, sizeof (LVITEMW));
|
|
|
|
item.mask = LVIF_STATE;
|
|
|
|
item.state = 0;
|
|
|
|
item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
|
|
|
|
if (SendMessageW(hwnd, LVM_SETITEMSTATE, (WPARAM) current, (LPARAM) (&item)) == FALSE)
|
|
|
|
xpanic("error deselecting current Table item", GetLastError());
|
|
|
|
}
|
2014-08-18 01:58:30 -05:00
|
|
|
if (index == -1) // select nothing
|
|
|
|
return;
|
2014-08-18 01:22:27 -05:00
|
|
|
ZeroMemory(&item, sizeof (LVITEMW));
|
|
|
|
item.mask = LVIF_STATE;
|
|
|
|
item.state = LVIS_FOCUSED | LVIS_SELECTED;
|
|
|
|
item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
|
|
|
|
if (SendMessageW(hwnd, LVM_SETITEMSTATE, (WPARAM) index, (LPARAM) (&item)) == FALSE)
|
|
|
|
xpanic("error selecting new Table item", GetLastError());
|
|
|
|
}
|