From 844da8d3122472d32ef09b14bfd7c5b5123a834b Mon Sep 17 00:00:00 2001 From: Angelo Haller Date: Mon, 8 Jul 2019 16:53:50 -0500 Subject: [PATCH] Add new API for sort related operations. Add functions to get/set table header sort indicators: uiTableHeaderSortIndicator uiTableHeaderSetSortIndicator Add callback setter for table header clicks: uiTableHeaderOnClicked --- darwin/table.h | 2 ++ darwin/table.m | 49 ++++++++++++++++++++++++++++++++++ test/page16.c | 17 ++++++++++++ ui.h | 16 +++++++++++ unix/table.c | 59 +++++++++++++++++++++++++++++++++++++++++ windows/table.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++++- windows/table.hpp | 2 ++ 7 files changed, 211 insertions(+), 1 deletion(-) diff --git a/darwin/table.h b/darwin/table.h index 4146ab71..edca49f4 100644 --- a/darwin/table.h +++ b/darwin/table.h @@ -16,6 +16,8 @@ struct uiTable { uiprivScrollViewData *d; int backgroundColumn; uiTableModel *m; + void (*headerOnClicked)(uiTable *, int, void *); + void *headerOnClickedData; }; // tablecolumn.m diff --git a/darwin/table.m b/darwin/table.m index 1bdc7f8d..a13bfd40 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -16,6 +16,7 @@ uiTableModel *uiprivM; } - (id)initWithFrame:(NSRect)r uiprivT:(uiTable *)t uiprivM:(uiTableModel *)m; +- (uiTable *)uiTable; @end @implementation uiprivTableView @@ -30,6 +31,11 @@ return self; } +- (uiTable *)uiTable +{ + return self->uiprivT; +} + // TODO is this correct for overflow scrolling? static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger row) { @@ -88,6 +94,12 @@ static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger setBackgroundColor((uiprivTableView *) tv, rv, row); } +- (void)tableView:(uiprivTableView *)tv didClickTableColumn:(NSTableColumn *) tc +{ + uiTable *t = [tv uiTable]; + t->headerOnClicked(t, [[tc identifier] intValue], t->headerOnClickedData); +} + @end uiTableModel *uiNewTableModel(uiTableModelHandler *mh) @@ -170,6 +182,17 @@ static void uiTableDestroy(uiControl *c) uiFreeControl(uiControl(t)); } +void uiTableHeaderOnClicked(uiTable *t, void (*f)(uiTable *, int, void *), void *data) +{ + t->headerOnClicked = f; + t->headerOnClickedData = data; +} + +static void defaultHeaderOnClicked(uiTable *table, int column, void *data) +{ + // do nothing +} + uiTable *uiNewTable(uiTableParams *p) { uiTable *t; @@ -209,6 +232,8 @@ uiTable *uiNewTable(uiTableParams *p) sp.VScroll = YES; t->sv = uiprivMkScrollView(&sp, &(t->d)); + uiTableHeaderOnClicked(t, defaultHeaderOnClicked, NULL); + // TODO WHY DOES THIS REMOVE ALL GRAPHICAL GLITCHES? // I got the idea from http://jwilling.com/blog/optimized-nstableview-scrolling/ but that was on an unrelated problem I didn't seem to have (although I have small-ish tables to start with) // I don't get layer-backing... am I supposed to layer-back EVERYTHING manually? I need to check Interface Builder again... @@ -216,3 +241,27 @@ uiTable *uiNewTable(uiTableParams *p) return t; } + +uiSort uiTableHeaderSortIndicator(uiTable *t, int lcol) +{ + NSTableColumn *tc = [t->tv tableColumnWithIdentifier:[@(lcol) stringValue]]; + NSString *si = [[t->tv indicatorImageInTableColumn:tc] name]; + if ([si isEqualToString:@"NSAscendingSortIndicator"]) + return uiSortAscending; + else if ([si isEqualToString:@"NSDescendingSortIndicator"]) + return uiSortDescending; + return uiSortNone; +} + +void uiTableHeaderSetSortIndicator(uiTable *t, int lcol, uiSort order) +{ + NSTableColumn *tc = [t->tv tableColumnWithIdentifier:[@(lcol) stringValue]]; + NSImage *img; + if (order == uiSortAscending) + img = [NSImage imageNamed:@"NSAscendingSortIndicator"]; + else if (order == uiSortDescending) + img = [NSImage imageNamed:@"NSDescendingSortIndicator"]; + else + img = nil; + [t->tv setIndicatorImage:img inTableColumn:tc]; +} diff --git a/test/page16.c b/test/page16.c index f28ba3c7..4a97ba00 100644 --- a/test/page16.c +++ b/test/page16.c @@ -100,6 +100,21 @@ static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, static uiTableModel *m; +static void headerOnClicked(uiTable *t, int col, void *data) +{ + static int prev = 0; + + if (prev != col) + uiTableHeaderSetSortIndicator(t, prev, uiSortNone); + + if (uiTableHeaderSortIndicator(t, col) == uiSortAscending) + uiTableHeaderSetSortIndicator(t, col, uiSortDescending); + else + uiTableHeaderSetSortIndicator(t, col, uiSortAscending); + + prev = col; +} + uiBox *makePage16(void) { uiBox *page16; @@ -152,6 +167,8 @@ uiBox *makePage16(void) uiTableAppendProgressBarColumn(t, "Progress Bar", 8); + uiTableHeaderOnClicked(t, headerOnClicked, NULL); + return page16; } diff --git a/ui.h b/ui.h index 36706f24..0bdf380d 100644 --- a/ui.h +++ b/ui.h @@ -1464,6 +1464,22 @@ _UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, // uiNewTable() creates a new uiTable with the specified parameters. _UI_EXTERN uiTable *uiNewTable(uiTableParams *params); +// uiTableHeaderSetSortIndicator() sets the sort indicator of the table +// header to display an appropriate arrow on the column header +_UI_EXTERN void uiTableHeaderSetSortIndicator(uiTable *t, + int column, + uiSort order); + +// uiTableHeaderSortIndicator returns the sort indicator of the specified +// column +_UI_EXTERN uiSort uiTableHeaderSortIndicator(uiTable *t, int column); + +// uiTableHeaderOnClicked() sets a callback function to be called +// when a table column header is clicked +_UI_EXTERN void uiTableHeaderOnClicked(uiTable *t, + void (*f)(uiTable *table, int column, void *data), + void *data); + #ifdef __cplusplus } #endif diff --git a/unix/table.c b/unix/table.c index e29ada07..7fb7f2db 100644 --- a/unix/table.c +++ b/unix/table.c @@ -18,6 +18,8 @@ struct uiTable { // TODO document this properly GHashTable *indeterminatePositions; guint indeterminateTimer; + void (*headerOnClicked)(uiTable *, int, void *); + void *headerOnClickedData; }; // use the same size as GtkFileChooserWidget's treeview @@ -327,6 +329,59 @@ static void buttonColumnClicked(GtkCellRenderer *r, gchar *pathstr, gpointer dat onEdited(p->m, p->modelColumn, pathstr, NULL, NULL); } +uiSort uiTableHeaderSortIndicator(uiTable *t, int lcol) +{ + GtkTreeViewColumn *c = gtk_tree_view_get_column(t->tv, lcol); + + if (c == NULL || gtk_tree_view_column_get_sort_indicator(c) == FALSE) + return uiSortNone; + + if (gtk_tree_view_column_get_sort_order(c) == GTK_SORT_ASCENDING) + return uiSortAscending; + else + return uiSortDescending; +} + +void uiTableHeaderSetSortIndicator(uiTable *t, int lcol, uiSort order) +{ + GtkTreeViewColumn *c = gtk_tree_view_get_column(t->tv, lcol); + + if (c == NULL) + return; + + if (order == uiSortNone) { + gtk_tree_view_column_set_sort_indicator(c, FALSE); + return; + } + + gtk_tree_view_column_set_sort_indicator(c, TRUE); + if (order == uiSortAscending) + gtk_tree_view_column_set_sort_order(c, GTK_SORT_ASCENDING); + else + gtk_tree_view_column_set_sort_order(c, GTK_SORT_DESCENDING); +} + +void uiTableHeaderOnClicked(uiTable *t, void (*f)(uiTable *, int, void *), void *data) +{ + t->headerOnClicked = f; + t->headerOnClickedData = data; +} + +static void defaultHeaderOnClicked(uiTable *table, int column, void *data) +{ + // do nothing +} + +static void headerOnClicked(GtkTreeViewColumn *c, gpointer data) +{ + guint i; + uiTable *t = uiTable(data); + + for (i = 0; i < gtk_tree_view_get_n_columns(t->tv); ++i) + if (gtk_tree_view_get_column(t->tv, i) == c) + t->headerOnClicked(t, i, t->headerOnClickedData); +} + static GtkTreeViewColumn *addColumn(uiTable *t, const char *name) { GtkTreeViewColumn *c; @@ -334,6 +389,8 @@ static GtkTreeViewColumn *addColumn(uiTable *t, const char *name) c = gtk_tree_view_column_new(); gtk_tree_view_column_set_resizable(c, TRUE); gtk_tree_view_column_set_title(c, name); + gtk_tree_view_column_set_clickable(c, 1); + g_signal_connect(c, "clicked", G_CALLBACK(headerOnClicked), t); gtk_tree_view_append_column(t->tv, c); return c; } @@ -520,5 +577,7 @@ uiTable *uiNewTable(uiTableParams *p) t->indeterminatePositions = g_hash_table_new_full(rowcolHash, rowcolEqual, uiprivFree, uiprivFree); + uiTableHeaderOnClicked(t, defaultHeaderOnClicked, NULL); + return t; } diff --git a/windows/table.cpp b/windows/table.cpp index 85a51c1a..143d1a04 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -230,6 +230,59 @@ int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG return progress; } +void uiTableHeaderSetSortIndicator(uiTable *t, int column, uiSort order) +{ + HWND lvhdr; + int fmt; + + if (order == uiSortAscending) + fmt = HDF_SORTUP; + else if (order == uiSortDescending) + fmt = HDF_SORTDOWN; + else + fmt = 0; + + lvhdr = ListView_GetHeader(t->hwnd); + if (lvhdr) { + HDITEM hdri = {}; + hdri.mask = HDI_FORMAT; + if (Header_GetItem(lvhdr, column, &hdri)) { + hdri.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); + hdri.fmt |= fmt; + Header_SetItem(lvhdr, column, &hdri); + } + } +} + +uiSort uiTableHeaderSortIndicator(uiTable *t, int column) +{ + HWND lvhdr; + + lvhdr = ListView_GetHeader(t->hwnd); + if (lvhdr) { + HDITEM hdri = {}; + hdri.mask = HDI_FORMAT; + if (Header_GetItem(lvhdr, column, &hdri)) { + if (hdri.fmt & HDF_SORTUP) + return uiSortAscending; + if (hdri.fmt & HDF_SORTDOWN) + return uiSortDescending; + } + } + return uiSortNone; +} + +void uiTableHeaderOnClicked(uiTable *t, void (*f)(uiTable *table, int column, void *data), void *data) +{ + t->headerOnClicked = f; + t->headerOnClickedData = data; +} + +static void defaultHeaderOnClicked(uiTable *table, int column, void *data) +{ + // do nothing +} + // TODO properly integrate compound statements static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { @@ -310,9 +363,20 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) } return FALSE; } + case LVN_COLUMNCLICK: + { + NMLISTVIEW *nm = (NMLISTVIEW *) nmhdr; + + hr = uiprivTableFinishEditingText(t); + if (hr != S_OK) { + // TODO + return FALSE; + } + t->headerOnClicked(t, nm->iSubItem, t->headerOnClickedData); + return TRUE; + } // 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 @@ -490,6 +554,7 @@ uiTable *uiNewTable(uiTableParams *p) t->columns = new std::vector; t->model = p->Model; t->backgroundColumn = p->RowBackgroundColorModelColumn; + uiTableHeaderOnClicked(t, defaultHeaderOnClicked, NULL); // WS_CLIPCHILDREN is here to prevent drawing over the edit box used for editing text t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, diff --git a/windows/table.hpp b/windows/table.hpp index 71946d62..b5297ae8 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -40,6 +40,8 @@ struct uiTable { HWND edit; int editedItem; int editedSubitem; + void (*headerOnClicked)(uiTable *, int, void *); + void *headerOnClickedData; }; extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos);