sclass JConceptsTable { Class conceptClass; Concepts concepts; JTable table; // options L dropFields; IF1 postProcess; Runnable afterUpdate; bool latestFirst; IF1> sorter; int idWidth = 50; int updateInterval = 1000; bool humanizeFieldNames = true; Float tableFontSize; *() {} *(Class *conceptClass) {} *(Concepts *concepts, Class *conceptClass) {} swappable MapSO itemToMap(A a) { ret mapValues renderForTable_noStruct(allConceptFieldsAsMap(a)); } S defaultTitle() { ret plural(shortClassName(conceptClass)); } void showAsFrame(S title default defaultTitle()) { makeTable(); showFrame(title, table); } void makeTable { if (table != null) ret; if (concepts == null) concepts = db_mainConcepts(); table = sexyTable(); if (tableFontSize != null) setFontSize(tableFontSize, table); awtOnConceptChanges(table, updateInterval, r { new L data; Cl l = list(concepts, conceptClass); l = postProcess(sorter, l); for (A c : l) addIfNotNull(data, itemToMap(c)); if (latestFirst) reverseInPlace(data); data = (L) postProcess(postProcess, data); dataToTable_uneditable(data, table); if (humanizeFieldNames) humanizeTableColumns(); pcallF(afterUpdate); }); } void humanizeTableColumns { int n = tableColumnCount(table); for i to n: setColumnName(table, i, i == 0 ? "" : humanizeFormLabel(getColumnName(table, i))); }; JComponent visualize() { makeTable(); ret table; } }