diff options
| author | Pietro Gagliardi <[email protected]> | 2015-01-06 17:27:41 -0500 |
|---|---|---|
| committer | Pietro Gagliardi <[email protected]> | 2015-01-06 17:27:41 -0500 |
| commit | 22123fb676d26d309d428a892c453e35e684e367 (patch) | |
| tree | 9552f1a91a8b8995065e74573e8f59f448f2b08c /wintable/select.h | |
| parent | 720049bfd3c6d205859f3150dee33dee78dc3dc3 (diff) | |
Merged wintable/new/ into wintable/.
Diffstat (limited to 'wintable/select.h')
| -rw-r--r-- | wintable/select.h | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/wintable/select.h b/wintable/select.h new file mode 100644 index 0000000..0539e52 --- /dev/null +++ b/wintable/select.h @@ -0,0 +1,255 @@ +// 13 december 2014 + +// damn winsock +static void doselect(struct table *t, intptr_t row, intptr_t column) +{ + RECT r, client; + intptr_t oldrow; + LONG width, height; + struct rowcol rc; + BOOL dovscroll; + intptr_t i; + intptr_t xpos; + LONG clientWidth; + + // check existing selection to see if it's valid + if (t->selectedRow == -1 && t->selectedColumn != -1) + panic("sanity check failure: old Table selection invalid (row == -1, column != -1)"); + if (t->selectedRow != -1 && t->selectedColumn == -1) + panic("sanity check failure: old Table selection invalid (row != -1, column == -1)"); + if (t->selectedRow >= t->count) + panic("sanity check failure: old Table selection invalid (row out of range)"); + if (t->selectedColumn >= t->nColumns) + panic("sanity check failure: old Table selection invalid (column out of range)"); + + oldrow = t->selectedRow; + t->selectedRow = row; + t->selectedColumn = column; + + // check new selection to see if it's valid + if (t->selectedRow == -1 && t->selectedColumn != -1) + panic("sanity check failure: new Table selection invalid (row == -1, column != -1)"); + if (t->selectedRow != -1 && t->selectedColumn == -1) + panic("sanity check failure: new Table selection invalid (row != -1, column == -1)"); + if (t->selectedRow >= t->count) + panic("sanity check failure: new Table selection invalid (row out of range)"); + if (t->selectedColumn >= t->nColumns) + panic("sanity check failure: new Table selection invalid (column out of range)"); + + // do this even if we don't scroll before; noScroll depends on it + if (GetClientRect(t->hwnd, &client) == 0) + panic("error getting Table client rect in doselect()"); + client.top += t->headerHeight; + height = rowht(t); + + // only scroll if we selected something + if (t->selectedRow == -1 || t->selectedColumn == -1) + goto noScroll; + + // first vertically scroll to the new row to make it fully visible (or as visible as possible) + if (t->selectedRow < t->vscrollpos) + vscrollto(t, t->selectedRow); + else { + rc.row = t->selectedRow; + rc.column = t->selectedColumn; + // first assume entirely outside the client area + dovscroll = TRUE; + if (rowColumnToClientRect(t, rc, &r)) + // partially outside the client area? + if (r.bottom <= client.bottom) // <= here since we are comparing bottoms (which are the first pixels outside the rectangle) + dovscroll = FALSE; + if (dovscroll) + vscrollto(t, t->selectedRow - t->vpagesize + 1); // + 1 because apparently just t->selectedRow - t->vpagesize results in no scrolling (t->selectedRow - t->vpagesize == t->vscrollpos)... + } + + // now see if the cell we want is to the left of offscreen, in which case scroll to its x-position + xpos = 0; + for (i = 0; i < t->selectedColumn; i++) + xpos += columnWidth(t, i); + if (xpos < t->hscrollpos) + hscrollto(t, xpos); + else { + // if the full cell is not visible, scroll to the right just enough to make it fully visible (or as visible as possible) + width = columnWidth(t, t->selectedColumn); + clientWidth = client.right - client.left; + if (xpos + width > t->hscrollpos + clientWidth) // > because both sides deal with the first pixel outside + // if the column is too wide, then just make it occupy the whole visible area (left-aligned) + if (width > clientWidth) // TODO >= ? + hscrollto(t, xpos); + else + // TODO don't use t->hpagesize here? depends if other code uses it + hscrollto(t, (xpos + width) - t->hpagesize); + } + +noScroll: + // now redraw the old and new /rows/ + // we do this after scrolling so the rectangles to be invalidated make sense + r.left = client.left; + r.right = client.right; + if (oldrow != -1 && oldrow >= t->vscrollpos) { + r.top = client.top + ((oldrow - t->vscrollpos) * height); + r.bottom = r.top + height; + if (InvalidateRect(t->hwnd, &r, TRUE) == 0) + panic("error queueing previously selected row for redraw in doselect()"); + } + // t->selectedRow must be visible by this point; we scrolled to it + if (t->selectedRow != -1 && t->selectedRow != oldrow) { + r.top = client.top + ((t->selectedRow - t->vscrollpos) * height); + r.bottom = r.top + height; + if (InvalidateRect(t->hwnd, &r, TRUE) == 0) + panic("error queueing newly selected row for redraw in doselect()"); + } +} + +// TODO make this needless +HANDLER(checkboxMouseDownHandler); + +// TODO which WM_xBUTTONDOWNs? +HANDLER(mouseDownSelectHandler) +{ + struct rowcol rc; + + rc = lParamToRowColumn(t, lParam); + // don't check if lParamToRowColumn() returned row -1 or column -1; we want deselection behavior + doselect(t, rc.row, rc.column); + // TODO separate this from here + checkboxMouseDownHandler(t, uMsg, wParam, lParam, lResult); + *lResult = 0; + return TRUE; +} + +/* +the routine below is intended to simulate the comctl32.dll listview keyboard navigation rules, at least as far as vertical navigation is concerned. +horizontal scrolling is different because unlike the comctl32 listview, we say that a single column in each row has the keyboard focus, so left and right navigate between columns here, instead of scrolling left/right by pixels. + TODO provide an override for scrolling by pixels? + TODO any other keyboard shortcuts? + TODO browser keys + TODO media navigation keys + TODO XBUTTON1/2? + TODO clear keys? + +keyboard selection behaviors of the windows 7 listview: +with 100 items (0-99), the window currently shows items 30 through 47 as well as having item 48 partially visible +- item 30: + - page up -> item 13 + - page down -> item 47 +- item 31: + - page up -> item 30 + - page down -> item 47 +- item 42: + - page up -> item 30 + - page down -> item 47 +- item 46: + - page up -> item 30 + - page down -> item 47 +- item 47: + - page up: -> item 30 + - page down: -> item 64 + +when nothing is selected: +- down selects item 0 regardless of scroll +- up selects nothing regardless of scroll +- page down selects the last fully visible item depending on scroll + - so with the above configuration: + - item 0 -> item 17 + - item 30 -> item 47 + - item 80 -> item 97 +- page up selects item 0 regardless of scroll +- home selects item 0 regardless of scroll +- end selects the last item regardless of scroll + +for left and right we will simulate up and down, respectively (so right selects row 0 column 0); remember that you can't have either row or column be -1 but not both + +TODO what happens if page up and page down are pressed with an item selected and the scroll in a different position? +*/ + +HANDLER(keyDownSelectHandler) +{ + intptr_t row; + intptr_t column; + + if (t->count == 0 || t->nColumns == 0) // no items to select + return FALSE; + row = t->selectedRow; + column = t->selectedColumn; + switch (wParam) { + case VK_UP: + if (row == -1) + return FALSE; + row--; + if (row < 0) + row = 0; + break; + case VK_DOWN: + if (row == -1) { + row = 0; + column = 0; + } else { + row++; + if (row >= t->count) + row = t->count - 1; + } + break; + case VK_LEFT: + if (column == -1) + return FALSE; + column--; + if (column < 0) + column = 0; + break; + case VK_RIGHT: + if (column == -1) { + row = 0; + column = 0; + } else { + column++; + if (column >= t->nColumns) + column = t->nColumns - 1; + } + break; + case VK_HOME: + row = 0; + if (column == -1) + column = 0; + break; + case VK_END: + row = t->count - 1; + if (column == -1) + column = 0; + break; + case VK_PRIOR: + if (row == -1) { + row = 0; + column = 0; + } else { + row = t->vscrollpos; + if (row == t->selectedRow) + // TODO investigate why the - 1 is needed here and below + // TODO if this is a misunderstanding of how t->vpagesize works, figure out what happens if there is no partially visible row, and what is supposed to happen + row -= t->vpagesize - 1; + if (row < 0) + row = 0; + } + break; + case VK_NEXT: + if (row == -1) { + row = t->vscrollpos + t->vpagesize - 1; + // TODO ensusre this is the case with the real list view + if (row >= t->count) + row = t->count - 1; + column = 0; + } else { + row = t->vscrollpos + t->vpagesize - 1; + if (row == t->selectedRow) + row += t->vpagesize - 1; + if (row >= t->count) + row = t->count - 1; + } + break; + default: + return FALSE; + } + doselect(t, row, column); + *lResult = 0; + return TRUE; +} |
