Handled the finished-editing cases I can right now. It is... mostly good???????????? IListView is very tantalizing now...
This commit is contained in:
parent
ff4b424ab0
commit
25a443f4f2
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
// general TODOs:
|
// general TODOs:
|
||||||
// - tooltips don't work properly on columns with icons (the listview always thinks there's enough room for a short label because it's not taking the icon into account); is this a bug in our LVN_GETDISPINFO handler or something else?
|
// - tooltips don't work properly on columns with icons (the listview always thinks there's enough room for a short label because it's not taking the icon into account); is this a bug in our LVN_GETDISPINFO handler or something else?
|
||||||
|
// - should clicking on some other column of the same row, even one that doesn't edit, cancel editing?
|
||||||
|
|
||||||
static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = {
|
static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = {
|
||||||
/*TODO.ColorModelColumn = */-1,
|
/*TODO.ColorModelColumn = */-1,
|
||||||
|
@ -83,8 +84,14 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
|
||||||
static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData)
|
static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData)
|
||||||
{
|
{
|
||||||
uiTable *t = (uiTable *) dwRefData;
|
uiTable *t = (uiTable *) dwRefData;
|
||||||
|
NMHDR *nmhdr = (NMHDR *) lParam;
|
||||||
|
bool finishEdit, abortEdit;
|
||||||
|
HWND header;
|
||||||
LRESULT lResult;
|
LRESULT lResult;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
finishEdit = false;
|
||||||
|
abortEdit = false;
|
||||||
switch (uMsg) {
|
switch (uMsg) {
|
||||||
case WM_TIMER:
|
case WM_TIMER:
|
||||||
if (wParam == (WPARAM) (&(t->inDoubleClickTimer))) {
|
if (wParam == (WPARAM) (&(t->inDoubleClickTimer))) {
|
||||||
|
@ -107,11 +114,69 @@ static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM
|
||||||
lResult = DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
lResult = DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||||
t->inLButtonDown = FALSE;
|
t->inLButtonDown = FALSE;
|
||||||
return lResult;
|
return lResult;
|
||||||
|
case WM_COMMAND:
|
||||||
|
if (HIWORD(wParam) == EN_UPDATE) {
|
||||||
|
// the real list view resizes the edit control on this notification specifically
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
// the real list view accepts changes in this case
|
||||||
|
if (HIWORD(wParam) == EN_KILLFOCUS)
|
||||||
|
finishEdit = true;
|
||||||
|
break; // don't override default handling
|
||||||
|
case WM_NOTIFY:
|
||||||
|
// list view accepts changes on column resize, but does not provide such notifications :/
|
||||||
|
header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0);
|
||||||
|
if (nmhdr->hwndFrom == header) {
|
||||||
|
NMHEADERW *nm = (NMHEADERW *) nmhdr;
|
||||||
|
|
||||||
|
switch (nmhdr->code) {
|
||||||
|
case HDN_ITEMCHANGED:
|
||||||
|
if ((nm->pitem->mask & HDI_WIDTH) == 0)
|
||||||
|
break;
|
||||||
|
// fall through
|
||||||
|
case HDN_DIVIDERDBLCLICK:
|
||||||
|
case HDN_TRACK:
|
||||||
|
case HDN_ENDTRACK:
|
||||||
|
finishEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// I think this mirrors the WM_COMMAND one above... TODO
|
||||||
|
if (nmhdr->code == NM_KILLFOCUS)
|
||||||
|
finishEdit = true;
|
||||||
|
break; // don't override default handling
|
||||||
|
case LVM_CANCELEDITLABEL:
|
||||||
|
finishEdit = true;
|
||||||
|
// TODO properly imitate notifiactions
|
||||||
|
break; // don't override default handling
|
||||||
|
// TODO finish edit on WM_WINDOWPOSCHANGING and WM_SIZE?
|
||||||
|
// for the next three: this item is about to go away; don't bother keeping changes
|
||||||
|
case LVM_SETITEMCOUNT:
|
||||||
|
if (wParam <= t->editedItem)
|
||||||
|
abortEdit = true;
|
||||||
|
break; // don't override default handling
|
||||||
|
case LVM_DELETEITEM:
|
||||||
|
if (wParam == t->editedItem)
|
||||||
|
abortEdit = true;
|
||||||
|
break; // don't override default handling
|
||||||
|
case LVM_DELETEALLITEMS:
|
||||||
|
abortEdit = true;
|
||||||
|
break; // don't override default handling
|
||||||
case WM_NCDESTROY:
|
case WM_NCDESTROY:
|
||||||
if (RemoveWindowSubclass(hwnd, tableSubProc, uIDSubclass) == FALSE)
|
if (RemoveWindowSubclass(hwnd, tableSubProc, uIDSubclass) == FALSE)
|
||||||
logLastError(L"RemoveWindowSubclass()");
|
logLastError(L"RemoveWindowSubclass()");
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
|
if (finishEdit) {
|
||||||
|
hr = uiprivTableFinishEditingText(t);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
} else if (abortEdit) {
|
||||||
|
hr = uiprivTableAbortEditingText(t);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +222,7 @@ int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG
|
||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO properly integrate compound statements
|
||||||
static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
|
static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
|
||||||
{
|
{
|
||||||
uiTable *t = uiTable(c);
|
uiTable *t = uiTable(c);
|
||||||
|
@ -208,12 +274,14 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
|
||||||
{
|
{
|
||||||
NMLISTVIEW *nm = (NMLISTVIEW *) nmhdr;
|
NMLISTVIEW *nm = (NMLISTVIEW *) nmhdr;
|
||||||
UINT oldSelected, newSelected;
|
UINT oldSelected, newSelected;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
if (!t->inLButtonDown)
|
// TODO clean up these if cases
|
||||||
|
if (!t->inLButtonDown && t->edit == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
oldSelected = nm->uOldState & LVIS_SELECTED;
|
oldSelected = nm->uOldState & LVIS_SELECTED;
|
||||||
newSelected = nm->uNewState & LVIS_SELECTED;
|
newSelected = nm->uNewState & LVIS_SELECTED;
|
||||||
if (oldSelected == 0 && newSelected != 0) {
|
if (t->inLButtonDown && oldSelected == 0 && newSelected != 0) {
|
||||||
t->inDoubleClickTimer = TRUE;
|
t->inDoubleClickTimer = TRUE;
|
||||||
// TODO check error
|
// TODO check error
|
||||||
SetTimer(t->hwnd, (UINT_PTR) (&(t->inDoubleClickTimer)),
|
SetTimer(t->hwnd, (UINT_PTR) (&(t->inDoubleClickTimer)),
|
||||||
|
@ -221,8 +289,29 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
|
||||||
*lResult = 0;
|
*lResult = 0;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
// the nm->iItem == -1 case is because that is used if "the change has been applied to all items in the list view"
|
||||||
|
if (t->edit != NULL && oldSelected != 0 && newSelected == 0 && (t->editedItem == nm->iItem || nm->iItem == -1)) {
|
||||||
|
// TODO see if the real list view accepts or rejects changes here; Windows Explorer accepts
|
||||||
|
hr = uiprivTableFinishEditingText(t);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
// TODO
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*lResult = 0;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
// the real list view accepts changes when scrolling or clicking column headers
|
||||||
|
case LVN_BEGINSCROLL:
|
||||||
|
case LVN_COLUMNCLICK:
|
||||||
|
hr = uiprivTableFinishEditingText(t);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
// TODO
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*lResult = 0;
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -232,7 +321,12 @@ static void uiTableDestroy(uiControl *c)
|
||||||
uiTable *t = uiTable(c);
|
uiTable *t = uiTable(c);
|
||||||
uiTableModel *model = t->model;
|
uiTableModel *model = t->model;
|
||||||
std::vector<uiTable *>::iterator it;
|
std::vector<uiTable *>::iterator it;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
hr = uiprivTableAbortEditingText(t);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd);
|
uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd);
|
||||||
uiWindowsEnsureDestroyWindow(t->hwnd);
|
uiWindowsEnsureDestroyWindow(t->hwnd);
|
||||||
// detach table from model
|
// detach table from model
|
||||||
|
|
|
@ -36,6 +36,8 @@ struct uiTable {
|
||||||
BOOL inLButtonDown;
|
BOOL inLButtonDown;
|
||||||
BOOL inDoubleClickTimer;
|
BOOL inDoubleClickTimer;
|
||||||
HWND edit;
|
HWND edit;
|
||||||
|
int editedItem;
|
||||||
|
int editedSubitem;
|
||||||
};
|
};
|
||||||
extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos);
|
extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos);
|
||||||
|
|
||||||
|
@ -48,3 +50,5 @@ extern HRESULT uiprivUpdateImageListSize(uiTable *t);
|
||||||
|
|
||||||
// tableevents.cpp
|
// tableevents.cpp
|
||||||
extern HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult);
|
extern HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult);
|
||||||
|
extern HRESULT uiprivTableFinishEditingText(uiTable *t);
|
||||||
|
extern HRESULT uiprivTableAbortEditingText(uiTable *t);
|
||||||
|
|
|
@ -17,6 +17,41 @@ static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG l
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the real list view intercepts these keys to control editing
|
||||||
|
static LRESULT CALLBACK editSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData)
|
||||||
|
{
|
||||||
|
uiTable *t = (uiTable *) dwRefData;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
switch (uMsg) {
|
||||||
|
case WM_KEYDOWN:
|
||||||
|
switch (wParam) {
|
||||||
|
// TODO handle VK_TAB and VK_SHIFT+VK_TAB
|
||||||
|
case VK_RETURN:
|
||||||
|
hr = uiprivTableFinishEditingText(t);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
return 0; // yes, the real list view just returns here
|
||||||
|
case VK_ESCAPE:
|
||||||
|
hr = uiprivTableAbortEditingText(t);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// the real list view also forces these flags
|
||||||
|
case WM_GETDLGCODE:
|
||||||
|
return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
|
||||||
|
case WM_NCDESTROY:
|
||||||
|
if (RemoveWindowSubclass(hwnd, editSubProc, uIDSubclass) == FALSE)
|
||||||
|
logLastError(L"RemoveWindowSubclass()");
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableColumnParams *p)
|
static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableColumnParams *p)
|
||||||
{
|
{
|
||||||
RECT itemLabel;
|
RECT itemLabel;
|
||||||
|
@ -27,6 +62,11 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC
|
||||||
LONG xInflate, yInflate;
|
LONG xInflate, yInflate;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
|
// the real list view accepts changes to the existing item when editing a new item
|
||||||
|
hr = uiprivTableFinishEditingText(t);
|
||||||
|
if (hr != S_OK)
|
||||||
|
return hr;
|
||||||
|
|
||||||
// compute this in advance so we don't have to needlessly call DestroyWindow() later
|
// compute this in advance so we don't have to needlessly call DestroyWindow() later
|
||||||
// TODO deduplicate this code with tabledraw.cpp
|
// TODO deduplicate this code with tabledraw.cpp
|
||||||
// TODO check LRESULT bad parameters here
|
// TODO check LRESULT bad parameters here
|
||||||
|
@ -132,6 +172,27 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC
|
||||||
ShowWindow(t->edit, SW_SHOWDEFAULT);
|
ShowWindow(t->edit, SW_SHOWDEFAULT);
|
||||||
|
|
||||||
uiprivFree(wstr);
|
uiprivFree(wstr);
|
||||||
|
t->editedItem = iItem;
|
||||||
|
t->editedSubitem = iSubItem;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT uiprivTableFinishEditingText(uiTable *t)
|
||||||
|
{
|
||||||
|
if (t->edit == NULL)
|
||||||
|
return S_OK;
|
||||||
|
return uiprivTableAbortEditingText(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT uiprivTableAbortEditingText(uiTable *t)
|
||||||
|
{
|
||||||
|
if (t->edit == NULL)
|
||||||
|
return S_OK;
|
||||||
|
if (DestroyWindow(t->edit) == 0) {
|
||||||
|
logLastError(L"DestroyWindow()");
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
t->edit = NULL;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue