346 // ...selection model
347 setSelectionModel(new ListView.ListViewBitSetSelectionModel<T>(this));
348
349 // ...focus model
350 setFocusModel(new ListView.ListViewFocusModel<T>(this));
351
352 // ...edit commit handler
353 setOnEditCommit(DEFAULT_EDIT_COMMIT_HANDLER);
354
355 // Fix for RT-36651, which was introduced by RT-35679 (above) and resolved
356 // by having special-case code to remove the listener when requested.
357 // This is done by ComboBoxListViewSkin, so that selection is not done
358 // when a ComboBox is shown.
359 getProperties().addListener((MapChangeListener<Object, Object>) change -> {
360 if (change.wasAdded() && "selectFirstRowByDefault".equals(change.getKey())) {
361 Boolean _selectFirstRowByDefault = (Boolean) change.getValueAdded();
362 if (_selectFirstRowByDefault == null) return;
363 selectFirstRowByDefault = _selectFirstRowByDefault;
364 }
365 });
366 }
367
368
369
370 /***************************************************************************
371 * *
372 * Callbacks and Events *
373 * *
374 **************************************************************************/
375
376 private EventHandler<ListView.EditEvent<T>> DEFAULT_EDIT_COMMIT_HANDLER = t -> {
377 int index = t.getIndex();
378 List<T> list = getItems();
379 if (index < 0 || index >= list.size()) return;
380 list.set(index, t.getNewValue());
381 };
382
383
384
385 /***************************************************************************
386 * *
387 * Properties *
388 * *
389 **************************************************************************/
390
391 // --- Items
392 private ObjectProperty<ObservableList<T>> items;
393
394 /**
395 * Sets the underlying data model for the ListView. Note that it has a generic
396 * type that must match the type of the ListView itself.
397 * @param value the list of items for this ListView
398 */
399 public final void setItems(ObservableList<T> value) {
400 itemsProperty().set(value);
401 }
402
1211
1212
1213 // package for testing
1214 static class ListViewBitSetSelectionModel<T> extends MultipleSelectionModelBase<T> {
1215
1216 /***********************************************************************
1217 * *
1218 * Constructors *
1219 * *
1220 **********************************************************************/
1221
1222 public ListViewBitSetSelectionModel(final ListView<T> listView) {
1223 if (listView == null) {
1224 throw new IllegalArgumentException("ListView can not be null");
1225 }
1226
1227 this.listView = listView;
1228
1229 ((SelectedItemsReadOnlyObservableList)getSelectedItems()).setItemsList(listView.getItems());
1230
1231
1232 /*
1233 * The following two listeners are used in conjunction with
1234 * SelectionModel.select(T obj) to allow for a developer to select
1235 * an item that is not actually in the data model. When this occurs,
1236 * we actively try to find an index that matches this object, going
1237 * so far as to actually watch for all changes to the items list,
1238 * rechecking each time.
1239 */
1240 itemsObserver = new InvalidationListener() {
1241 private WeakReference<ObservableList<T>> weakItemsRef = new WeakReference<>(listView.getItems());
1242
1243 @Override public void invalidated(Observable observable) {
1244 ObservableList<T> oldItems = weakItemsRef.get();
1245 weakItemsRef = new WeakReference<>(listView.getItems());
1246 ((SelectedItemsReadOnlyObservableList)getSelectedItems()).setItemsList(listView.getItems());
1247 updateItemsObserver(oldItems, listView.getItems());
1248 }
1249 };
1250
1251 this.listView.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
1343 // System.out.println("\tWas replaced");
1344 // }
1345 // if (c.wasPermutated()) {
1346 // System.out.println("\tWas permutated");
1347 // }
1348 c.reset();
1349
1350 List<Pair<Integer, Integer>> shifts = new ArrayList<>();
1351 while (c.next()) {
1352 if (c.wasReplaced()) {
1353 if (c.getList().isEmpty()) {
1354 // the entire items list was emptied - clear selection
1355 clearSelection();
1356 } else {
1357 int index = getSelectedIndex();
1358
1359 if (previousModelSize == c.getRemovedSize()) {
1360 // all items were removed from the model
1361 clearSelection();
1362 } else if (index < getItemCount() && index >= 0) {
1363 // Fix for RT-18969: the list had setAll called on it
1364 // Use of makeAtomic is a fix for RT-20945
1365 startAtomic();
1366 clearSelection(index);
1367 stopAtomic();
1368 select(index);
1369 } else {
1370 // Fix for RT-22079
1371 clearSelection();
1372 }
1373 }
1374 } else if (c.wasAdded() || c.wasRemoved()) {
1375 int shift = c.wasAdded() ? c.getAddedSize() : -c.getRemovedSize();
1376 shifts.add(new Pair<>(c.getFrom(), shift));
1377 } else if (c.wasPermutated()) {
1378
1379 // General approach:
1380 // -- detected a sort has happened
1381 // -- Create a permutation lookup map (1)
1382 // -- dump all the selected indices into a list (2)
1383 // -- clear the selected items / indexes (3)
|
346 // ...selection model
347 setSelectionModel(new ListView.ListViewBitSetSelectionModel<T>(this));
348
349 // ...focus model
350 setFocusModel(new ListView.ListViewFocusModel<T>(this));
351
352 // ...edit commit handler
353 setOnEditCommit(DEFAULT_EDIT_COMMIT_HANDLER);
354
355 // Fix for RT-36651, which was introduced by RT-35679 (above) and resolved
356 // by having special-case code to remove the listener when requested.
357 // This is done by ComboBoxListViewSkin, so that selection is not done
358 // when a ComboBox is shown.
359 getProperties().addListener((MapChangeListener<Object, Object>) change -> {
360 if (change.wasAdded() && "selectFirstRowByDefault".equals(change.getKey())) {
361 Boolean _selectFirstRowByDefault = (Boolean) change.getValueAdded();
362 if (_selectFirstRowByDefault == null) return;
363 selectFirstRowByDefault = _selectFirstRowByDefault;
364 }
365 });
366
367 sceneProperty().addListener((o, oldScene, newScene) -> {
368 if (oldScene != null) {
369 oldScene.focusOwnerProperty().removeListener(weakFocusOwnerListener);
370 }
371 if (newScene != null) {
372 newScene.focusOwnerProperty().addListener(weakFocusOwnerListener);
373 }
374 });
375 }
376
377
378
379 /***************************************************************************
380 * *
381 * Callbacks and Events *
382 * *
383 **************************************************************************/
384
385 private EventHandler<ListView.EditEvent<T>> DEFAULT_EDIT_COMMIT_HANDLER = t -> {
386 int index = t.getIndex();
387 List<T> list = getItems();
388 if (index < 0 || index >= list.size()) return;
389 list.set(index, t.getNewValue());
390 };
391
392 private InvalidationListener focusOwnerListener = o -> {
393 if (!ControlUtils.isFocusOnNodeOrAnyChild(this)) {
394 edit(-1);
395 }
396 };
397 private WeakInvalidationListener weakFocusOwnerListener = new WeakInvalidationListener(focusOwnerListener);
398
399
400
401 /***************************************************************************
402 * *
403 * Properties *
404 * *
405 **************************************************************************/
406
407 // --- Items
408 private ObjectProperty<ObservableList<T>> items;
409
410 /**
411 * Sets the underlying data model for the ListView. Note that it has a generic
412 * type that must match the type of the ListView itself.
413 * @param value the list of items for this ListView
414 */
415 public final void setItems(ObservableList<T> value) {
416 itemsProperty().set(value);
417 }
418
1227
1228
1229 // package for testing
1230 static class ListViewBitSetSelectionModel<T> extends MultipleSelectionModelBase<T> {
1231
1232 /***********************************************************************
1233 * *
1234 * Constructors *
1235 * *
1236 **********************************************************************/
1237
1238 public ListViewBitSetSelectionModel(final ListView<T> listView) {
1239 if (listView == null) {
1240 throw new IllegalArgumentException("ListView can not be null");
1241 }
1242
1243 this.listView = listView;
1244
1245 ((SelectedItemsReadOnlyObservableList)getSelectedItems()).setItemsList(listView.getItems());
1246
1247 /*
1248 * The following two listeners are used in conjunction with
1249 * SelectionModel.select(T obj) to allow for a developer to select
1250 * an item that is not actually in the data model. When this occurs,
1251 * we actively try to find an index that matches this object, going
1252 * so far as to actually watch for all changes to the items list,
1253 * rechecking each time.
1254 */
1255 itemsObserver = new InvalidationListener() {
1256 private WeakReference<ObservableList<T>> weakItemsRef = new WeakReference<>(listView.getItems());
1257
1258 @Override public void invalidated(Observable observable) {
1259 ObservableList<T> oldItems = weakItemsRef.get();
1260 weakItemsRef = new WeakReference<>(listView.getItems());
1261 ((SelectedItemsReadOnlyObservableList)getSelectedItems()).setItemsList(listView.getItems());
1262 updateItemsObserver(oldItems, listView.getItems());
1263 }
1264 };
1265
1266 this.listView.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
1358 // System.out.println("\tWas replaced");
1359 // }
1360 // if (c.wasPermutated()) {
1361 // System.out.println("\tWas permutated");
1362 // }
1363 c.reset();
1364
1365 List<Pair<Integer, Integer>> shifts = new ArrayList<>();
1366 while (c.next()) {
1367 if (c.wasReplaced()) {
1368 if (c.getList().isEmpty()) {
1369 // the entire items list was emptied - clear selection
1370 clearSelection();
1371 } else {
1372 int index = getSelectedIndex();
1373
1374 if (previousModelSize == c.getRemovedSize()) {
1375 // all items were removed from the model
1376 clearSelection();
1377 } else if (index < getItemCount() && index >= 0) {
1378 // Use of makeAtomic is a fix for RT-20945
1379 startAtomic();
1380 clearSelection(index);
1381 stopAtomic();
1382 select(index);
1383 } else {
1384 // Fix for RT-22079
1385 clearSelection();
1386 }
1387 }
1388 } else if (c.wasAdded() || c.wasRemoved()) {
1389 int shift = c.wasAdded() ? c.getAddedSize() : -c.getRemovedSize();
1390 shifts.add(new Pair<>(c.getFrom(), shift));
1391 } else if (c.wasPermutated()) {
1392
1393 // General approach:
1394 // -- detected a sort has happened
1395 // -- Create a permutation lookup map (1)
1396 // -- dump all the selected indices into a list (2)
1397 // -- clear the selected items / indexes (3)
|