sclass JConceptsTable implements Swingable { Class conceptClass; Concepts concepts; JTable table; // options S hID = "ID"; // Column header for concept ID settable LS dropFields; IF1 postProcess; Runnable afterUpdate; bool latestFirst; IF1> sorter = lambda1 defaultSort; int idWidth = 50; int updateInterval = 100; int firstUpdateInterval = 100; bool humanizeFieldNames = true; Float tableFontSize; Int tableRowHeight; settable bool addCountToEnclosingTab; AWTOnConceptChanges changeHandler; *() {} *(Class *conceptClass) {} *(Concepts *concepts, Class *conceptClass) {} swappable MapSO itemToMap(A a) { ret putAll(specialFieldsForItem(a), mapValues renderForTable_noStruct(itemToMap_inner2(a))); } swappable MapSO itemToMap_inner2(A a) { ret allConceptFieldsAsMapExcept(a, dropFields); } // shown on the left (usually) swappable MapSO specialFieldsForItem(A a) { MapSO map = litorderedmap(hID, str(a.id)); mapPut(map, "Java Class", javaClassDescForItem(a)); ret map; } S javaClassDescForItem(A a) { S className = dynShortClassName(a); if (neq(className, shortClassName(conceptClass))) { S text = className; S realClass = shortClassName(a); if (neq(className, realClass)) text += " as " + realClass; ret text; } null; } 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) { setTableFontSizes(tableFontSize, table); if (tableRowHeight == null) tableRowHeight = iround(tableFontSize*1.5); } if (tableRowHeight != null) setRowHeight(table, tableRowHeight); changeHandler = new AWTOnConceptChanges(concepts, table, lambda0 _update) .delay(updateInterval) .firstDelay(firstUpdateInterval); changeHandler.install(); } // e.g. to update enclosing tab when hidden void update { swing { _update(); } } void _update { 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(); tableColumnMaxWidth(table, 0, idWidth); if (addCountToEnclosingTab) updateEnclosingTabTitle(); pcallF(afterUpdate); } void updateEnclosingTabTitle { updateEnclosingTabTitleWithCount(table, countConcepts(concepts, conceptClass)); } void humanizeTableColumns { int n = tableColumnCount(table); for i to n: setColumnName(table, i, humanizeFormLabel(getColumnName(table, i))); }; visual table(); JTable table() { makeTable(); ret table; } A selectedConcept() { ret (A) concepts.getConcept(toLong(selectedTableCell(table, 0))); } A selected() { ret selectedConcept(); } A getItem(int row) { ret (A) concepts.getConcept(toLong(getTableCell(table, row, 0))); } int indexOfConcept(final A c) { if (c == null) ret -1; ret swing(func -> int { int n = tableRowCount(table); for row to n: if (toLong(getTableCell(table, row, 0)) == c.id) ret row; ret -1; }); } L selectedConcepts() { int[] rows = table.getSelectedRows(); new L l; for (int row : rows) l.add(getItem(row)); ret l; } Cl defaultSort(Cl l) { ret sortByConceptID(l); } /*void dropFields(S... fields) { dropFields = asList(fields); }*/ }