sclass JConceptsTable implements Swingable { Class conceptClass; Concepts concepts; JTable table; // options MapSO filters; // fields to filter by/add to new objects S hID = "ID"; // Column header for concept ID settable LS dropFields; IF1 postProcess; Runnable afterUpdate; bool latestFirst; IF1> sorter = lambda1 defaultSort; int idWidth = 50; settable int updateInterval = 100; int firstUpdateInterval = 100; bool humanizeFieldNames = true; Float tableFontSize; Int tableRowHeight; settable bool addCountToEnclosingTab; settable bool useNewChangeHandler; AWTOnConceptChanges changeHandler; AWTOnConceptChangesByClass newChangeHandler; *() {} *(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); if (useNewChangeHandler) { newChangeHandler = new AWTOnConceptChangesByClass(concepts, conceptClass, table, lambda0 _update) .delay(updateInterval) .firstDelay(firstUpdateInterval); newChangeHandler.install(); } else { 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 { if (table == null) ret; new L data; Set selection = toSet(selectedConceptIDs()); Cl l = conceptsWhere(concepts, conceptClass, mapToParams(filters)); 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); restoreSelection(selection); 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(); } long getItemID(int row) { ret toLong(getTableCell(table, row, 0)); } A getItem(int row) { ret (A) concepts.getConcept(getItemID(row)); } 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() { ret swing(-> { int[] rows = table.getSelectedRows(); new L l; for (int row : rows) l.add(getItem(row)); ret l; }); } L selectedConceptIDs() { ret swing(-> { int[] rows = table.getSelectedRows(); L l = emptyList(l(rows)); for (int row : rows) l.add(getItemID(row)); ret l; }); } void restoreSelection(Set selection) swing { int n = tableRowCount(table); new IntBuffer toSelect; for row to n: if (selection.contains(getItemID(row))) toSelect.add(row); selectTableRows(table, toSelect.toIntArray()); } Cl defaultSort(Cl l) { ret sortByConceptID(l); } selfType addFilter(S field, O value) { filters = orderedMapPutOrCreate(filters, field, value); this; } // call AFTER table was made void onSelectionChanged(Runnable r) { if (table == null) warn("Call onSelectionChanged after making table"); onTableSelectionChanged(table, r); } void onSelectionChangedAndNow(Runnable r) { if (r == null) ret; onSelectionChanged(r); r.run(); } }