1 /* GuiList.cpp
2  *
3  * Copyright (C) 1993-2020 Paul Boersma, 2013 Tom Naughton
4  *
5  * This code is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  *
10  * This code is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this work. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "GuiP.h"
20 
21 Thing_implement (GuiList, GuiControl, 0);
22 
23 #if motif
24 	#define iam_list \
25 		Melder_assert (widget -> widgetClass == xmListWidgetClass); \
26 		GuiList me = (GuiList) widget -> userData
27 #endif
28 
29 #if gtk
_GuiGtkList_destroyCallback(gpointer void_me)30 	static void _GuiGtkList_destroyCallback (gpointer void_me) {
31 		iam (GuiList);
32 		forget (me);
33 	}
_GuiGtkList_selectionChangedCallback(GtkTreeSelection * sel,gpointer void_me)34 	static void _GuiGtkList_selectionChangedCallback (GtkTreeSelection *sel, gpointer void_me) {
35 		iam (GuiList);
36 		if (my d_selectionChangedCallback && ! my d_blockValueChangedCallbacks) {
37 			trace (U"Selection changed.");
38 			struct structGuiList_SelectionChangedEvent event { me };
39 			my d_selectionChangedCallback (my d_selectionChangedBoss, & event);
40 		}
41 	}
42 #elif motif
_GuiWinList_destroy(GuiObject widget)43 	void _GuiWinList_destroy (GuiObject widget) {
44 		iam_list;
45 		DestroyWindow (widget -> window);
46 		forget (me);   // NOTE: my widget is not destroyed here
47 	}
_GuiWinList_map(GuiObject widget)48 	void _GuiWinList_map (GuiObject widget) {
49 		iam_list;
50 		ShowWindow (widget -> window, SW_SHOW);
51 	}
_GuiWinList_handleClick(GuiObject widget)52 	void _GuiWinList_handleClick (GuiObject widget) {
53 		iam_list;
54 		if (my d_selectionChangedCallback) {
55 			struct structGuiList_SelectionChangedEvent event { me };
56 			my d_selectionChangedCallback (my d_selectionChangedBoss, & event);
57 		}
58 	}
59 #elif cocoa
60 	@implementation GuiCocoaList {
61 		GuiList d_userData;
62 	}
63 
64 	/*
65 	 * Override NSObject methods.
66 	 */
67 	- (void) dealloc {
68 		[_contents release];
69 		GuiThing me = d_userData;
70 		forget (me);
71 		//Melder_casual (U"deleting a list");
72 		[super dealloc];
73 	}
74 
75 	/*
76 	 * Override NSView methods.
77 	 */
78 	- (id) initWithFrame: (NSRect) frameRect {
79 		self = [super initWithFrame: frameRect];
80 		if (self) {
81 			_tableView = [[NSTableView alloc] initWithFrame: frameRect];
82 			NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier: @"list"];
83 			tableColumn.width = frameRect. size. width;
84 			[tableColumn setEditable: NO];
85 			[_tableView addTableColumn: tableColumn];
86 
87 			_tableView. delegate = self;
88 			_tableView. dataSource = self;
89 			_tableView. allowsEmptySelection = YES;
90 			_tableView. headerView = nil;
91 			_tableView. target = self;
92 			_tableView. action = @selector (_GuiCocoaList_clicked:);
93 
94 			NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: frameRect];
95 			[scrollView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
96 			[scrollView setBorderType: NSBezelBorder];
97 			[scrollView setDocumentView: _tableView];   // this retains the table view
98 			[scrollView setHasVerticalScroller: YES];
99 			//[scrollView setHasHorizontalScroller: YES];
100 
101 			[self addSubview: scrollView];   // this retains the scroll view
102 			[scrollView release];
103 			[_tableView release];
104 
105 			_contents = [[NSMutableArray alloc] init];
106 		}
107 		return self;
108 	}
109 
110 	/*
111 	 * Implement GuiCocoaAny protocol.
112 	 */
113 	- (GuiThing) getUserData {
114 		return d_userData;
115 	}
116 	- (void) setUserData: (GuiThing) userData {
117 		Melder_assert (userData == nullptr || Thing_isa (userData, classGuiList));
118 		d_userData = static_cast <GuiList> (userData);
119 	}
120 
121 	/*
122 	 * Implement GuiCocoaList methods.
123 	 */
124 	- (IBAction) _GuiCocoaList_clicked: (id) sender {
125 		/*
126 		 * This method probably shouldn't do anything,
127 		 * because tableViewSelectionDidChange will already have been called at this point.
128 		 */
129 		(void) sender;
130 		trace (U"enter");
131 		GuiList me = d_userData;
132 		if (me && my d_selectionChangedCallback) {
133 			//struct structGuiList_SelectionChangedEvent event { me };
134 			//my d_selectionChangedCallback (my d_selectionChangedBoss, & event);
135 		}
136 	}
137 
138 	/*
139 	 * Override TableViewDataSource methods.
140 	 */
141 	- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView {
142 		(void) tableView;
143 		return [_contents count];
144 	}
145 	- (id) tableView:  (NSTableView *) tableView   objectValueForTableColumn: (NSTableColumn *) tableColumn   row: (NSInteger) row {
146 		(void) tableColumn;
147 		(void) tableView;
148 		return [_contents   objectAtIndex: row];
149 	}
150 
151 	/*
152 	 * Override TableViewDelegate methods.
153 	 */
154 	- (void) tableViewSelectionDidChange: (NSNotification *) notification {
155 		/*
156 		 * This is invoked when the user clicks in the table or uses the arrow keys.
157 		 */
158 		(void) notification;
159 		trace (U"enter");
160 		GuiList me = d_userData;
161 		if (me && my d_selectionChangedCallback && ! my d_blockValueChangedCallbacks) {
162 			struct structGuiList_SelectionChangedEvent event { me };
163 			my d_selectionChangedCallback (my d_selectionChangedBoss, & event);
164 		}
165 	}
166 	@end
167 #endif
168 
169 #if gtk
170 	enum {
171 	  COLUMN_STRING,
172 	  N_COLUMNS
173 	};
174 #endif
175 
GuiList_create(GuiForm parent,int left,int right,int top,int bottom,bool allowMultipleSelection,conststring32 header)176 GuiList GuiList_create (GuiForm parent, int left, int right, int top, int bottom, bool allowMultipleSelection, conststring32 header) {
177 	autoGuiList me = Thing_new (GuiList);
178 	my d_shell = parent -> d_shell;
179 	my d_parent = parent;
180 	my d_allowMultipleSelection = allowMultipleSelection;
181 	#if gtk
182 		GtkCellRenderer *renderer = nullptr;
183 		GtkTreeViewColumn *col = nullptr;
184 		GtkTreeSelection *sel = nullptr;
185 		GtkListStore *liststore = nullptr;
186 
187 		liststore = gtk_list_store_new (1, G_TYPE_STRING);   // 1 column, of type String (this is a vararg list)
188 		GuiObject scrolled = gtk_scrolled_window_new (nullptr, nullptr);
189 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
190 		my d_widget = gtk_tree_view_new_with_model (GTK_TREE_MODEL (liststore));
191 		gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (my d_widget), false);
192 		gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (my d_widget));
193 		gtk_widget_show (GTK_WIDGET (scrolled));   // BUG
194 		gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (my d_widget), allowMultipleSelection ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE);
195 		g_object_unref (liststore);   // Destroys the widget after the list is destroyed
196 
197 		_GuiObject_setUserData (my d_widget, me.get());
198 		_GuiObject_setUserData (scrolled, me.get());   // for resizing
199 
200 		renderer = gtk_cell_renderer_text_new ();
201 		col = gtk_tree_view_column_new ();
202 		gtk_tree_view_column_pack_start (col, renderer, true);
203 		gtk_tree_view_column_add_attribute (col, renderer, "text", 0);   // zeroeth column
204 		if (header) {
205 			//gtk_tree_view_column_set_title (col, Melder_peek32to8 (header));
206 		}
207 		gtk_tree_view_append_column (GTK_TREE_VIEW (my d_widget), col);
208 
209 		g_object_set_data_full (G_OBJECT (my d_widget), "guiList", me.get(), (GDestroyNotify) _GuiGtkList_destroyCallback);
210 
211 /*		GtkCellRenderer *renderer;
212 		GtkTreeViewColumn *col;
213 
214 		my widget = gtk_tree_view_new_with_model (GTK_TREE_MODEL (liststore));
215 
216 		renderer = gtk_cell_renderer_text_new ();
217 		col = gtk_tree_view_column_new ();
218 		gtk_tree_view_column_pack_start (col, renderer, true);
219 		gtk_tree_view_column_add_attribute (col, renderer, "text", COL_ID);
220 		gtk_tree_view_column_set_title (col, " ID ");
221 		gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
222 
223 		renderer = gtk_cell_renderer_text_new ();
224 		col = gtk_tree_view_column_new ();
225 		gtk_tree_view_column_pack_start (col, renderer, true);
226 		gtk_tree_view_column_add_attribute (col, renderer, "text", COL_TYPE);
227 		gtk_tree_view_column_set_title (col, " Type ");
228 		gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
229 
230 		renderer = gtk_cell_renderer_text_new ();
231 		col = gtk_tree_view_column_new ();
232 		gtk_tree_view_column_pack_start (col, renderer, true);
233 		gtk_tree_view_column_add_attribute (col, renderer, "text", COL_NAME);
234 		gtk_tree_view_column_set_title (col, " Name ");
235 		gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
236 */
237 
238 		sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (my d_widget));
239 		if (allowMultipleSelection) {
240 			gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
241 		} else {
242 			gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
243 		}
244 		my v_positionInForm (scrolled, left, right, top, bottom, parent);
245 		g_signal_connect (sel, "changed", G_CALLBACK (_GuiGtkList_selectionChangedCallback), me.get());
246 	#elif motif
247 		my d_widget = _Gui_initializeWidget (xmListWidgetClass, parent -> d_widget, U"list");
248 		_GuiObject_setUserData (my d_widget, me.get());
249 		my d_widget -> window = CreateWindowEx (0, L"listbox", L"list",
250 			WS_CHILD | WS_BORDER | WS_VSCROLL | LBS_NOTIFY | WS_CLIPSIBLINGS |
251 			( allowMultipleSelection ? LBS_EXTENDEDSEL : 0 ),
252 			my d_widget -> x, my d_widget -> y, my d_widget -> width, my d_widget -> height,
253 			my d_widget -> parent -> window, nullptr, theGui.instance, nullptr);
254 		SetWindowLongPtr (my d_widget -> window, GWLP_USERDATA, (LONG_PTR) my d_widget);
255 		SetWindowFont (my d_widget -> window, GetStockFont (ANSI_VAR_FONT), false);
256 		/*if (MEMBER (my parent, ScrolledWindow)) {
257 			XtDestroyWidget (my d_widget -> parent -> motiff.scrolledWindow.horizontalBar);
258 			my d_widget -> parent -> motiff.scrolledWindow.horizontalBar = nullptr;
259 			XtDestroyWidget (my d_widget -> parent -> motiff.scrolledWindow.verticalBar);
260 			my d_widget -> parent -> motiff.scrolledWindow.verticalBar = nullptr;
261 		}*/
262 		my v_positionInForm (my d_widget, left, right, top, bottom, parent);
263 	#elif cocoa
264 		(void) header;
265 		GuiCocoaList *list = [[GuiCocoaList alloc] init];
266 		my d_widget = (GuiObject) list;
267 		my v_positionInForm (my d_widget, left, right, top, bottom, parent);
268 		[[list tableView] setAllowsMultipleSelection: allowMultipleSelection];
269 		[list setUserData: me.get()];
270 	#endif
271 	return me.releaseToAmbiguousOwner();
272 }
273 
GuiList_createShown(GuiForm parent,int left,int right,int top,int bottom,bool allowMultipleSelection,conststring32 header)274 GuiList GuiList_createShown (GuiForm parent, int left, int right, int top, int bottom, bool allowMultipleSelection, conststring32 header) {
275 	GuiList me = GuiList_create (parent, left, right, top, bottom, allowMultipleSelection, header);
276 	GuiThing_show (me);
277 	return me;
278 }
279 
GuiList_deleteAllItems(GuiList me)280 void GuiList_deleteAllItems (GuiList me) {
281 	GuiControlBlockValueChangedCallbacks block (me);
282 	#if gtk
283 		GtkListStore *list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget)));
284 		gtk_list_store_clear (list_store);
285 	#elif motif
286 		ListBox_ResetContent (my d_widget -> window);
287 	#elif cocoa
288         GuiCocoaList *list = (GuiCocoaList *) my d_widget;
289         [list. contents   removeAllObjects];
290         [list. tableView   reloadData];
291 	#endif
292 }
293 
GuiList_deleteItem(GuiList me,integer position)294 void GuiList_deleteItem (GuiList me, integer position) {
295 	Melder_assert (position >= 1);   // so that we can subtract 1 even if the result has to be unsigned
296 	GuiControlBlockValueChangedCallbacks block (me);
297 	#if gtk
298 		GtkTreeIter iter;
299 		GtkTreeModel *tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget));
300 		if (gtk_tree_model_iter_nth_child (tree_model, & iter, nullptr, (gint) (position - 1))) {
301 			gtk_list_store_remove (GTK_LIST_STORE (tree_model), & iter);
302 		}
303 	#elif motif
304 		ListBox_DeleteString (my d_widget -> window, position - 1);
305 	#elif cocoa
306 		GuiCocoaList *list = (GuiCocoaList *) my d_widget;
307 		[list. contents   removeObjectAtIndex: (NSUInteger) (position - 1)];
308 		[list. tableView   reloadData];
309 	#endif
310 }
311 
GuiList_deselectAllItems(GuiList me)312 void GuiList_deselectAllItems (GuiList me) {
313 	GuiControlBlockValueChangedCallbacks block (me);
314 	#if gtk
315 		GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (my d_widget));
316 		gtk_tree_selection_unselect_all (selection);
317 	#elif motif
318 		ListBox_SetSel (my d_widget -> window, False, -1);
319 	#elif cocoa
320 		GuiCocoaList *list = (GuiCocoaList *) my d_widget;
321 		[list. tableView   deselectAll: nil];
322 	#endif
323 }
324 
GuiList_deselectItem(GuiList me,integer position)325 void GuiList_deselectItem (GuiList me, integer position) {
326 	Melder_assert (position >= 1);   // so that we can subtract 1 even if the result has to be unsigned
327 	GuiControlBlockValueChangedCallbacks block (me);
328 	#if gtk
329 		GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (my d_widget));
330 //		GtkListStore *list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget)));
331 //		GtkTreePath *path = gtk_tree_path_new_from_indices ((gint) position, -1 /* terminator */);
332 		GtkTreeIter iter;
333 //		gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), & iter, path);
334 //		gtk_tree_path_free (path);
335 		GtkTreeModel *tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget));
336 		if (gtk_tree_model_iter_nth_child (tree_model, & iter, nullptr, (gint) (position - 1))) {
337 			gtk_tree_selection_unselect_iter (selection, & iter);
338 		}
339 	#elif motif
340 		ListBox_SetSel (my d_widget -> window, False, position - 1);
341 	#elif cocoa
342 		GuiCocoaList *list = (GuiCocoaList *) my d_widget;
343 		[list. tableView   deselectRow: position - 1];
344 	#endif
345 }
346 
GuiList_getSelectedPositions(GuiList me)347 autoINTVEC GuiList_getSelectedPositions (GuiList me) {
348 	autoINTVEC selectedPositions;
349 	#if gtk
350 		GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (my d_widget));
351 		GtkListStore *list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget)));
352 		int n = gtk_tree_selection_count_selected_rows (selection);
353 		if (n > 0) {
354 			GList *list = gtk_tree_selection_get_selected_rows (selection, (GtkTreeModel **) & list_store);
355 			integer ipos = 1;
356 			selectedPositions = zero_INTVEC (n);
357 			for (GList *l = g_list_first (list); l != nullptr; l = g_list_next (l)) {
358 				gint *index = gtk_tree_path_get_indices ((GtkTreePath *) l -> data);
359 				selectedPositions [ipos] = index [0] + 1;
360 				ipos ++;
361 			}
362 			g_list_foreach (list, (GFunc) gtk_tree_path_free, nullptr);
363 			g_list_free (list);
364 		}
365 		return selectedPositions;
366 	#elif motif
367 		int n = ListBox_GetSelCount (my d_widget -> window), *indices;
368 		if (n == 0)
369 			return selectedPositions;
370 		if (n == -1) {   // single selection
371 			int selection = ListBox_GetCurSel (my d_widget -> window);
372 			if (selection == -1)
373 				return selectedPositions;
374 			n = 1;
375 			indices = Melder_calloc_f (int, n);
376 			indices [0] = selection;
377 		} else {
378 			indices = Melder_calloc_f (int, n);
379 			ListBox_GetSelItems (my d_widget -> window, n, indices);
380 		}
381 		selectedPositions = zero_INTVEC (n);
382 		for (integer ipos = 1; ipos <= n; ipos ++)
383 			selectedPositions [ipos] = indices [ipos - 1] + 1;   // convert from zero-based list of zero-based indices
384 		Melder_free (indices);
385 	#elif cocoa
386 		GuiCocoaList *list = (GuiCocoaList *) my d_widget;
387 		NSIndexSet *indexSet = [list. tableView   selectedRowIndexes];
388 		selectedPositions = zero_INTVEC (uinteger_to_integer ([indexSet count]));
389 		NSUInteger currentIndex = [indexSet firstIndex];
390 		integer ipos = 0;
391 		while (currentIndex != NSNotFound) {
392 			selectedPositions [++ ipos] = uinteger_to_integer (currentIndex + 1);
393 			currentIndex = [indexSet   indexGreaterThanIndex: currentIndex];
394 		}
395 		Melder_assert (ipos == selectedPositions.size);
396 	#endif
397 	return selectedPositions;
398 }
399 
GuiList_getBottomPosition(GuiList me)400 integer GuiList_getBottomPosition (GuiList me) {
401 	#if gtk
402 		GtkTreePath *path;
403 		integer position = 1;
404 		if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (my d_widget), nullptr, & path)) {
405 			int *indices = gtk_tree_path_get_indices (path);
406 			position = indices ? indices[0] + 1 : 1;
407 			gtk_tree_path_free (path); // also frees indices !!
408 		}
409 		trace (U"bottom: ", position);
410 		return position;
411 	#elif motif
412 		integer bottom = ListBox_GetTopIndex (my d_widget -> window) + my d_widget -> height / ListBox_GetItemHeight (my d_widget -> window, 0);
413 		if (bottom < 1) bottom = 1;
414 		integer n = ListBox_GetCount (my d_widget -> window);
415 		if (bottom > n) bottom = n;
416 		return bottom;
417 	#elif cocoa
418 		return 1;   // TODO
419 	#else
420 		return 0;
421 	#endif
422 }
423 
GuiList_getNumberOfItems(GuiList me)424 integer GuiList_getNumberOfItems (GuiList me) {
425 	integer numberOfItems = 0;
426 	#if gtk
427 		GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget));
428 		numberOfItems = gtk_tree_model_iter_n_children (model, nullptr);
429 	#elif motif
430 		numberOfItems = ListBox_GetCount (my d_widget -> window);
431 	#elif cocoa
432 		GuiCocoaList *list = (GuiCocoaList *) my d_widget;
433 		numberOfItems = [[list contents] count];
434 	#endif
435 	return numberOfItems;
436 }
437 
GuiList_getTopPosition(GuiList me)438 integer GuiList_getTopPosition (GuiList me) {
439 	#if gtk
440 		GtkTreePath *path;
441 		integer position = 1;
442 		if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (my d_widget), & path, nullptr)) {
443 			int *indices = gtk_tree_path_get_indices (path);
444 			position = indices ? indices[0] + 1 : 1;
445 			gtk_tree_path_free (path); // also frees indices !!
446 		}
447 		trace (U"top: ", position);
448 		return position;
449 	#elif motif
450 		integer top = ListBox_GetTopIndex (my d_widget -> window);
451 		if (top < 1) top = 1;
452 		integer n = ListBox_GetCount (my d_widget -> window);
453 		if (top > n) top = 0;
454 		return top;
455 	#elif cocoa
456 		return 1;   // TODO
457 	#else
458 		return 0;
459 	#endif
460 }
461 
GuiList_insertItem(GuiList me,conststring32 itemText,integer position_base1)462 void GuiList_insertItem (GuiList me, conststring32 itemText /* cattable */, integer position_base1) {
463 	bool explicitlyInsertAtEnd = ( position_base1 <= 0 );
464 	GuiControlBlockValueChangedCallbacks block (me);
465 	#if gtk
466 		GtkListStore *list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget)));
467 		gtk_list_store_insert_with_values (list_store, nullptr, explicitlyInsertAtEnd ? 1000000000 : (gint) position_base1 - 1, COLUMN_STRING, Melder_peek32to8 (itemText), -1);
468 		// TODO: Tekst opsplitsen
469 		// does GTK know the '0' trick?
470 		// it does know about nullptr, to append in another function
471 	#elif motif
472 		HWND nativeList = my d_widget -> window;
473 		conststringW nativeItemText = Melder_peek32toW (itemText);
474 		if (explicitlyInsertAtEnd) {
475 			ListBox_AddString (nativeList, nativeItemText);
476 		} else {
477 			int nativePosition_base0 = position_base1 - 1;
478 			ListBox_InsertString (nativeList, nativePosition_base0, nativeItemText);
479 		}
480 	#elif cocoa
481 		GuiCocoaList *nativeList = (GuiCocoaList *) my d_widget;
482 		NSString *nativeItemText = [[NSString alloc] initWithUTF8String: Melder_peek32to8 (itemText)];
483 		if (explicitlyInsertAtEnd) {
484 			[[nativeList contents]   addObject: nativeItemText];
485 		} else {
486 			NSUInteger nativePosition_base0 = (uinteger) position_base1 - 1;
487 			[[nativeList contents]   insertObject: nativeItemText   atIndex: nativePosition_base0];
488 		}
489 		[nativeItemText release];
490 		[[nativeList tableView] reloadData];
491 	#endif
492 }
493 
GuiList_replaceItem(GuiList me,conststring32 itemText,integer position)494 void GuiList_replaceItem (GuiList me, conststring32 itemText, integer position) {
495 	GuiControlBlockValueChangedCallbacks block (me);
496 	#if gtk
497 		GtkTreeIter iter;
498 		GtkTreeModel *tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget));
499 		if (gtk_tree_model_iter_nth_child (tree_model, & iter, nullptr, (gint) (position - 1))) {
500 			gtk_list_store_set (GTK_LIST_STORE (tree_model), & iter, COLUMN_STRING, Melder_peek32to8 (itemText), -1);
501 		}
502 /*
503 		GtkTreePath *path = gtk_tree_path_new_from_indices ((gint) position, -1);   // -1 = terminator
504 		GtkTreeIter iter;
505 		GtkListStore *list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget)));
506 		gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), & iter, path);
507 		gtk_tree_path_free (path);*/
508 		// gtk_list_store_set (list_store, & iter, 0, Melder_peek32to8 (itemText), -1);
509 		// TODO: Tekst opsplitsen
510 	#elif motif
511 		integer nativePosition = position - 1;   // convert from 1-based to zero-based
512 		ListBox_DeleteString (my d_widget -> window, nativePosition);
513 		ListBox_InsertString (my d_widget -> window, nativePosition, Melder_peek32toW (itemText));
514 	#elif cocoa
515 		GuiCocoaList *list = (GuiCocoaList *) my d_widget;
516 		NSString *nsString = [[NSString alloc] initWithUTF8String: Melder_peek32to8 (itemText)];
517 		[[list contents]   replaceObjectAtIndex: position - 1   withObject: nsString];
518 		[nsString release];
519 		[[list tableView] reloadData];
520 	#endif
521 }
522 
GuiList_selectItem(GuiList me,integer position)523 void GuiList_selectItem (GuiList me, integer position) {
524 	Melder_assert (position >= 1);   // so that we can subtract 1 even if the result has to be unsigned
525 	GuiControlBlockValueChangedCallbacks block (me);
526 	#if gtk
527 		GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (my d_widget));
528 		GtkTreePath *path = gtk_tree_path_new_from_indices ((gint) position - 1, -1 /* terminator */);
529 		gtk_tree_selection_select_path (selection, path);
530 		gtk_tree_path_free (path);
531 
532 // TODO: check of het bovenstaande werkt, dan kan dit weg
533 //		GtkListStore *list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (my d_widget)));
534 //		GtkTreePath *path = gtk_tree_path_new_from_indices ((gint) position, -1 /* terminator */);
535 //		GtkTreeIter iter;
536 //		gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), & iter, path);
537 //		gtk_tree_selection_select_iter (selection, & iter);
538 	#elif motif
539 		if (! my d_allowMultipleSelection) {
540 			ListBox_SetCurSel (my d_widget -> window, position - 1);
541 		} else {
542 			ListBox_SetSel (my d_widget -> window, True, position - 1);
543 		}
544 	#elif cocoa
545 		NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndex: NSUInteger (position - 1)];
546 		GuiCocoaList *list = (GuiCocoaList *) my d_widget;
547 		[[list tableView]   selectRowIndexes: indexSet   byExtendingSelection: my d_allowMultipleSelection];
548 		[indexSet release];
549 	#endif
550 }
551 
GuiList_setSelectionChangedCallback(GuiList me,GuiList_SelectionChangedCallback callback,Thing boss)552 void GuiList_setSelectionChangedCallback (GuiList me, GuiList_SelectionChangedCallback callback, Thing boss) {
553 	my d_selectionChangedCallback = callback;
554 	my d_selectionChangedBoss = boss;
555 }
556 
GuiList_setDoubleClickCallback(GuiList me,GuiList_DoubleClickCallback callback,Thing boss)557 void GuiList_setDoubleClickCallback (GuiList me, GuiList_DoubleClickCallback callback, Thing boss) {
558 	my d_doubleClickCallback = callback;
559 	my d_doubleClickBoss = boss;
560 }
561 
GuiList_setScrollCallback(GuiList me,GuiList_ScrollCallback callback,Thing boss)562 void GuiList_setScrollCallback (GuiList me, GuiList_ScrollCallback callback, Thing boss) {
563 	my d_scrollCallback = callback;
564 	my d_scrollBoss = boss;
565 }
566 
GuiList_setTopPosition(GuiList me,integer topPosition)567 void GuiList_setTopPosition (GuiList me, integer topPosition) {
568 	trace (U"Set top position ", topPosition);
569 	#if gtk
570 //		GtkListStore *list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (my md_widget)));
571 		GtkTreePath *path = gtk_tree_path_new_from_indices ((gint) topPosition, -1 /* terminator */);   // BUG?
572 		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (my d_widget), path, nullptr, false, 0.0, 0.0);
573 		gtk_tree_path_free (path);
574 	#elif motif
575 		ListBox_SetTopIndex (my d_widget -> window, topPosition - 1);
576 	#elif cocoa
577 	 // TODO: implement
578 	#endif
579 }
580 
581 /* End of file GuiList.cpp */
582