abstract sclass DynObjectTable extends DynModule {
new L data;
transient JTable table;
transient F1 itemToMap;
transient VF1 defaultAction;
transient bool debug, fieldsInOrder, withSearcher;
transient TableSearcher searcher;
transient L onListChanged;
start {
itemToMap = func(A a) -> Map {
a instanceof S ? litorderedmap("" := (S) a)
: humanizeKeys(fieldsInOrder ? objectToMap_inOrder(a) : objectToMap(a)) };
onChange(r updateTable);
}
visualize {
table = dataToTable_uneditable(sexyTable(), map(itemToMap, data));
onDoubleClickOrEnter(table, voidfunc(int row) {
temp enter();
A a = _get(data, row);
if (a != null) onDoubleClick(a);
});
if (withSearcher)
ret (searcher = tableWithSearcher2(table)).panel;
ret table;
}
void unvisualize() { super.unvisualize(); searcher = null; }
void onDoubleClick(A line) { callF(defaultAction, line); }
void updateTable() {
temp enter();
if (table != null) {
dataToTable_uneditable(table, map(itemToMap, data));
if (searcher != null) searcher.rowIndices = null;
if (debug) print("dataToTable done");
pcallFAll(onListChanged);
}
}
void dontPersist() { _persistenceInfo = mapPlus(_persistenceInfo, data := false); }
void clear() { syncClear(data); fireDataChanged(); }
void add(A a) { syncAdd(data, a); fireDataChanged(); }
void add(int idx, A a) { syncAdd(data, idx, a); fireDataChanged(); }
void addAll(Collection l) { if (empty(l)) ret; syncAddAll(data, l); fireDataChanged(); }
void addAndScrollDown(A a) { add(a); scrollTableDownNow(table); }
void remove(A a) { syncRemove(data, a); fireDataChanged(); }
void setList(Collection data) { setData(data); }
void setData(Collection data) {
int selection = selectedIndex();
if (isFalse(mapGet(_persistenceInfo, 'data))) {
if (neq(this.data, data)) {
this.data = cloneList(data);
updateTable();
}
} else
setField(data := cloneList(data));
selectRow(table, selection);
}
int count() { ret syncL(data); }
void setData_force(Collection data) {
this.data = null;
setData(data);
}
// tries to keep selection
void fireDataChanged() {
setData_force(data);
}
int rowFromSearcher(int i) { ret searcher == null ? i : or(get(searcher.rowIndices, i), -1); }
A selected() { ret syncGet(data, rowFromSearcher(selectedTableRowInModel(table))); }
int selectedIndex() { ret selectedTableRow(table); }
bool selectItem(A a) { int i = indexOf(data, a); selectRow(table, i); ret i >= 0; } // true if item exists
void doubleClickItem(A a) { if (selectItem(a)) onDoubleClick(a); }
// r : Runnable or VF1
// run only in visualize()!
void popupMenuItem(S text, O r) {
tablePopupMenuItemsThreaded(table, text, _convertRunnable(r));
}
void popupMenuItem_top(S text, O r) {
tablePopupMenuItemsThreaded_top(table, text, _convertRunnable(r));
}
// row index => element
O _convertRunnable(fO r) {
if (r == null || r instanceof Runnable) ret r;
ret voidfunc(int idx) { callF(r, syncGet(data, idx)) };
}
JTable table() { ret table; }
// API
L getData() { ret data; } // data is usually immutable
L data() { ret getData(); }
}