ui/win32/list.c

changeset 115
e57ca2747782
equal deleted inserted replaced
114:3da24640513a 115:e57ca2747782
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2025 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include <cx/array_list.h>
33
34 #include "list.h"
35 #include "container.h"
36
37
38
39 static W32WidgetClass listview_widget_class = {
40 .eventproc = ui_listview_eventproc,
41 .enable = w32_widget_default_enable,
42 .show = w32_widget_default_show,
43 .get_preferred_size = ui_listview_get_preferred_size,
44 .destroy = w32_widget_default_destroy
45 };
46
47 static void* strmodel_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
48 return col == 0 ? elm : NULL;
49 }
50
51 static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
52 ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata;
53 return getvalue(elm, col);
54 }
55
56 static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
57 return NULL;
58 }
59
60 /*
61 * Creates an UiListView widget object and initializes it from the UiListArgs
62 */
63 static UiListView* create_listview_widget(UiObject *obj, W32WidgetClass *widget_class, HWND hwnd, UiListArgs *args, UiBool table) {
64 UiListView *listview = w32_widget_create(widget_class, hwnd, sizeof(UiListView));
65 listview->widget.hwnd = hwnd;
66 listview->obj = obj;
67 listview->preferred_width = args->width ? args->width : 300; // 300: default width/height
68 listview->preferred_height = args->height ? args->height : 300;
69 listview->onactivate = args->onactivate;
70 listview->onactivatedata = args->onactivatedata;
71 listview->onselection = args->onselection;
72 listview->onselectiondata = args->onselectiondata;
73 listview->ondragstart = args->ondragstart;
74 listview->ondragstartdata = args->ondragstartdata;
75 listview->ondragcomplete = args->ondragcomplete;
76 listview->ondragcompletedata = args->ondragcompletedata;
77 listview->ondrop = args->ondrop;
78 listview->ondropdata = args->ondropdata;
79 listview->istable = table;
80
81 // convert ui_getvaluefunc into ui_getvaluefunc2 if necessary
82 ui_getvaluefunc2 getvalue = args->getvalue2;
83 void *getvaluedata = args->getvalue2data;
84 if(!getvalue) {
85 if(args->getvalue) {
86 getvalue = getvalue_wrapper;
87 getvaluedata = (void*)args->getvalue;
88 } else {
89 getvalue = table ? null_getvalue : strmodel_getvalue;
90 }
91 }
92 listview->getvalue = getvalue;
93 listview->getvaluedata = getvaluedata;
94
95 listview->var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
96
97 return listview;
98 }
99
100 static UIWIDGET listview_create(UiObject *obj, UiListArgs *args, UiBool table) {
101 HINSTANCE hInstance = GetModuleHandle(NULL);
102 UiContainerPrivate *container = ui_obj_container(obj);
103 HWND parent = ui_container_get_parent(container);
104 UiLayout layout = UI_ARGS2LAYOUT(args);
105
106 HWND hwnd = CreateWindowEx(
107 WS_EX_CLIENTEDGE,
108 WC_LISTVIEW,
109 "",
110 WS_CHILD | WS_VISIBLE | LVS_REPORT,
111 0, 0, 100, 100,
112 parent,
113 (HMENU)1337,
114 hInstance,
115 NULL);
116 ui_win32_set_ui_font(hwnd);
117 ListView_SetExtendedListViewStyle(
118 hwnd,
119 LVS_EX_FULLROWSELECT //| LVS_EX_GRIDLINES
120 );
121
122 UiListView *listview = create_listview_widget(obj, &listview_widget_class, hwnd, args, table);
123 ui_container_add(container, (W32Widget*)listview, &layout);
124
125 // init list model
126 // always initialize listview->model
127 int numcolumns = 0;
128 if (table) {
129 if (args->model) {
130 listview->model = ui_model_copy(obj->ctx, args->model);
131 numcolumns = listview->model->columns;
132 } else {
133 listview->model = ui_model_new(obj->ctx);
134 }
135 } else {
136 UiModel *model = ui_model_new(obj->ctx);
137 ui_model_add_column(model, UI_STRING, "Test", -1);
138 listview->model = model;
139 numcolumns = 1;
140 }
141
142 // create columns
143 UiModel *model = listview->model;
144 for (int i=0;i<numcolumns;i++) {
145 LVCOLUMN col;
146 UiModelType type = model->types[i];
147 char *title = model->titles[i];
148 size_t titlelen = title ? strlen(title) : 0;
149 int size = model->columnsize[i];
150 switch (type) {
151 default: {
152 col.mask = LVCF_TEXT | LVCF_WIDTH;
153 col.pszText = title;
154 col.cx = size > 0 ? size : titlelen*10+5;
155 break;
156 }
157 case UI_ICON: {
158 break; // TODO
159 }
160 }
161 ListView_InsertColumn(hwnd, i, &col);
162 }
163
164 // bind the listview to the provided UiList
165 if (listview->var) {
166 UiList *list = listview->var->value;
167 list->obj = listview;
168 list->update = ui_listview_update;
169 list->getselection = ui_listview_getselection_impl;
170 list->setselection = ui_listview_setselection_impl;
171
172 ui_listview_update(list, -1);
173 } else if (!table && args->static_elements && args->static_nelm > 0) {
174 char **static_elements = args->static_elements;
175 size_t static_nelm = args->static_nelm;
176 LVITEM item;
177 item.mask = LVIF_TEXT;
178 item.iSubItem = 0;
179 for (int i=0;i<static_nelm;i++) {
180 item.iItem = i;
181 item.pszText = static_elements[i];
182 ListView_InsertItem(hwnd, &item);
183 }
184 listview->getvalue = strmodel_getvalue;
185 listview->getvaluedata = NULL;
186 }
187
188 return (W32Widget*)listview;
189 }
190
191 static UiListSelection listview_get_selection2(HWND hwnd) {
192 UiListSelection sel = { 0, NULL };
193
194 CX_ARRAY_DECLARE(int, indices);
195 cx_array_initialize(indices, 8);
196
197 int index = -1;
198 while ((index = ListView_GetNextItem(hwnd, index, LVNI_SELECTED)) != -1) {
199 cx_array_simple_add(indices, index);
200 }
201
202 if (indices_size > 0) {
203 sel.rows = indices;
204 sel.count = indices_size;
205 }
206
207 return sel;
208 }
209
210 static UiListSelection listview_get_selection(UiListView *listview) {
211 HWND hwnd = listview->widget.hwnd;
212 return listview_get_selection2(hwnd);
213 }
214
215 // listview class event proc
216 int ui_listview_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
217 UiListView *listview = (UiListView*)widget;
218 switch (uMsg) {
219 case WM_NOTIFY: {
220 LPNMHDR hdr = (LPNMHDR)lParam;
221 switch (hdr->code) {
222 case LVN_ITEMCHANGED: {
223 LPNMLISTVIEW lv = (LPNMLISTVIEW)lParam;
224 int row = lv->iItem;
225 if ((lv->uChanged & LVIF_STATE) && (lv->uNewState & LVIS_SELECTED) && listview->onselection) {
226 UiListSelection sel = listview_get_selection(listview);
227
228 UiEvent event;
229 event.obj = listview->obj;
230 event.window = listview->obj->window;
231 event.document = listview->obj->ctx->document;
232 event.eventdata = &sel;
233 event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
234 event.intval = row;
235 event.set = ui_get_setop();
236 listview->onselection(&event, listview->onselectiondata);
237
238 ui_listselection_free(sel);
239 }
240 break;
241 }
242 case LVN_ITEMACTIVATE: {
243 LPNMLISTVIEW lv = (LPNMLISTVIEW)lParam;
244 int row = lv->iItem;
245 if (listview->onactivate) {
246 UiEvent event;
247 event.obj = listview->obj;
248 event.window = listview->obj->window;
249 event.document = listview->obj->ctx->document;
250 event.eventdata = NULL;
251 event.eventdatatype = UI_EVENT_DATA_LIST_ELM;
252 event.intval = row;
253 event.set = ui_get_setop();
254
255 if (listview->var) {
256 UiList *list = listview->var->value;
257 event.eventdata = list->get(list, row);
258 event.eventdatatype = UI_EVENT_DATA_LIST_ELM;
259 }
260
261 listview->onactivate(&event, listview->onactivatedata);
262 }
263 break;
264 }
265 }
266 break;
267 }
268 }
269
270 return 0;
271 }
272
273 W32Size ui_listview_get_preferred_size(W32Widget *widget) {
274 UiListView *listview = (UiListView*)widget;
275 W32Size size;
276 size.width = listview->preferred_width;
277 size.height = listview->preferred_height;
278 return size;
279 }
280
281 /*
282 * Creates and inserts an LVITEM
283 *
284 * list: An UiList bound to an UiListView
285 * row: row index
286 * elm: list element (same as list->get(list, row))
287 */
288 static void insert_item(UiList *list, int row, void *elm) {
289 UiListView *listview = (UiListView*)list->obj;
290 HWND hwnd = listview->widget.hwnd;
291 UiModel *model = listview->model;
292
293 LVITEM item;
294 item.mask = LVIF_TEXT;
295 item.iItem = row;
296 item.iSubItem = 0;
297 int idx = -1;
298 for (int col=0;col<model->columns;col++) {
299 UiBool freeResult = FALSE;
300 // convert the list element to a value, that can be displayed in the list view
301 // TODO: handle all model types
302 char *str = listview->getvalue(list, elm, row, col, listview->getvaluedata, &freeResult);
303 if (col == 0) {
304 item.pszText = str;
305 idx = ListView_InsertItem(hwnd, &item);
306 } else {
307 ListView_SetItemText(hwnd, idx, col, str);
308 }
309
310 if (freeResult) {
311 free(str);
312 }
313 }
314 }
315
316 /*
317 * UiList->update function
318 *
319 * Updates one or all rows
320 * row: list index or -1 for updating all rows
321 */
322 void ui_listview_update(UiList *list, int row) {
323 UiListView *listview = (UiListView*)list->obj;
324 HWND hwnd = listview->widget.hwnd;
325 UiModel *model = listview->model;
326 if (row < 0) {
327 ListView_DeleteAllItems(hwnd);
328 void *elm = list->first(list);
329 int row = 0;
330 while (elm) {
331 insert_item(list, row, elm);
332 elm = list->next(list);
333 row++;
334 }
335 } else {
336 ListView_DeleteItem(hwnd, row);
337 void *elm = list->get(list, row);
338 insert_item(list, row, elm);
339 }
340
341 // re-adjust all columns
342 for (int i=0;i<model->columns;i++) {
343 ListView_SetColumnWidth(hwnd, i, LVSCW_AUTOSIZE);
344 }
345 }
346
347 UiListSelection ui_listview_getselection_impl(UiList *list) {
348 UiListView *listview = (UiListView*)list->obj;
349 return listview_get_selection(listview);
350 }
351
352 void ui_listview_setselection_impl(UiList *list, UiListSelection selection) {
353
354 }
355
356 // public API
357 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
358 return listview_create(obj, args, FALSE);
359 }
360
361 // public API
362 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
363 return listview_create(obj, args, TRUE);
364 }
365
366 void ui_listview_select(UIWIDGET listview, int index) {
367
368 }
369
370 int ui_listview_selection(UIWIDGET listview) {
371 W32Widget *w = (W32Widget*)listview;
372 UiListSelection sel = listview_get_selection2(w->hwnd);
373 int index = -1;
374 if (sel.count > 0) {
375 index = sel.rows[0];
376 }
377 free(sel.rows);
378 return index;
379 }
380
381 /* ------------------------------------ DropDown ------------------------------------*/
382
383 static W32WidgetClass dropdown_widget_class = {
384 .eventproc = ui_dropdown_eventproc,
385 .enable = w32_widget_default_enable,
386 .show = w32_widget_default_show,
387 .get_preferred_size = ui_dropdown_get_preferred_size,
388 .destroy = w32_widget_default_destroy
389 };
390
391 UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
392 HINSTANCE hInstance = GetModuleHandle(NULL);
393 UiContainerPrivate *container = ui_obj_container(obj);
394 HWND parent = ui_container_get_parent(container);
395 UiLayout layout = UI_ARGS2LAYOUT(args);
396
397 HWND hwnd = CreateWindowEx(
398 WS_EX_CLIENTEDGE,
399 WC_COMBOBOX,
400 "",
401 WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWNLIST,
402 0, 0, 100, 100,
403 parent,
404 (HMENU)1337,
405 hInstance,
406 NULL);
407 ui_win32_set_ui_font(hwnd);
408
409 UiListView *dropdown = create_listview_widget(obj, &dropdown_widget_class, hwnd, args, FALSE);
410 ui_container_add(container, (W32Widget*)dropdown, &layout);
411
412 // bind the dropdown to the provided UiList
413 if (dropdown->var) {
414 UiList *list = dropdown->var->value;
415 list->obj = dropdown;
416 list->update = ui_dropdown_update;
417 list->getselection = ui_dropdown_getselection_impl;
418 list->setselection = ui_dropdown_setselection_impl;
419
420 ui_dropdown_update(list, -1);
421 } else if (args->static_elements && args->static_nelm > 0) {
422 char **static_elements = args->static_elements;
423 size_t static_nelm = args->static_nelm;
424 for (int i=0;i<static_nelm;i++) {
425 SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)static_elements[i]);
426 }
427 dropdown->getvalue = strmodel_getvalue;
428 dropdown->getvaluedata = NULL;
429 }
430
431 return (W32Widget*)dropdown;
432 }
433
434 int ui_dropdown_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
435 return 0;
436 }
437
438 W32Size ui_dropdown_get_preferred_size(W32Widget *widget) {
439 W32Size size;
440 size.width = 200;
441 size.height = 30;
442 return size;
443 }
444
445 static void dropdown_insert_item(UiList *list, int row, void *elm) {
446 UiListView *listview = (UiListView*)list->obj;
447 HWND hwnd = listview->widget.hwnd;
448
449 UiBool freeResult = FALSE;
450 char *str = listview->getvalue(list, elm, row, 0, listview->getvaluedata, &freeResult);
451 SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)str);
452
453 if (freeResult) {
454 free(str);
455 }
456 }
457
458 void ui_dropdown_update(UiList *list, int row) {
459 UiListView *listview = (UiListView*)list->obj;
460 HWND hwnd = listview->widget.hwnd;
461 if (row < 0) {
462 SendMessage(hwnd, CB_RESETCONTENT, 0, 0);
463
464 void *elm = list->first(list);
465 int row = 0;
466 while (elm) {
467 dropdown_insert_item(list, row, elm);
468 elm = list->next(list);
469 row++;
470 }
471 } else {
472 SendMessage(hwnd, CB_DELETESTRING, row, 0);
473 void *elm = list->get(list, row);
474 dropdown_insert_item(list, row, elm);
475 }
476 }
477
478 UiListSelection ui_dropdown_getselection_impl(UiList *list) {
479 UiListSelection sel = { 0, NULL };
480 UiListView *listview = (UiListView*)list->obj;
481 int index = (int)SendMessage(listview->widget.hwnd, CB_GETCURSEL, 0, 0);
482 if (index >= 0) {
483 sel.rows = malloc(sizeof(int));
484 sel.rows[0] = index;
485 sel.count = 1;
486 }
487 return sel;
488 }
489
490 void ui_dropdown_setselection_impl(UiList *list, UiListSelection selection) {
491 UiListView *listview = (UiListView*)list->obj;
492 SendMessage(listview->widget.hwnd, CB_SETCURSEL, 0, 0);
493 }
494
495 void ui_dropdown_select(UIWIDGET dropdown, int index) {
496 SendMessage(dropdown->hwnd, CB_SETCURSEL, 0, 0);
497 }
498
499 int ui_dropdown_selection(UIWIDGET dropdown) {
500 return SendMessage(dropdown->hwnd, CB_GETCURSEL, 0, 0);
501 }

mercurial