summaryrefslogtreecommitdiff
path: root/wintable/select.h
diff options
context:
space:
mode:
authorPietro Gagliardi <[email protected]>2015-01-06 17:27:41 -0500
committerPietro Gagliardi <[email protected]>2015-01-06 17:27:41 -0500
commit22123fb676d26d309d428a892c453e35e684e367 (patch)
tree9552f1a91a8b8995065e74573e8f59f448f2b08c /wintable/select.h
parent720049bfd3c6d205859f3150dee33dee78dc3dc3 (diff)
Merged wintable/new/ into wintable/.
Diffstat (limited to 'wintable/select.h')
-rw-r--r--wintable/select.h255
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;
+}