!7 sclass FollowUp > DynModule { start { dm_useLocalMechListCopies(); } visualize { final Set followUps = asHashSet(map dropLeadingHash(extractSingleStarMatches("* is a follow-up message", concatLists(mL("Facts about Telegram messages"), lines((S) call(dm_get_newFacts(), 'text)))))); new L data; for (TelegramMsg msg : serverTelegramLog()) if (nempty(msg.globalID)) data.add(ll(msg.globalID, escapeNewLines(msg.text), followUps.contains(msg.globalID))); final L cols = ll("Global ID", "Text", "Is Follow-Up"); final JTable table = sexyTable(); Pair p = fillTableWithData_makeData(data, cols); setTableModel(table, new DefaultTableModel(p.a, p.b) { public Class getColumnClass(int column) { return or(_getClass(getValueAt(0, column)), S.class); } public bool isCellEditable(int rowIndex, int columnIndex) { ret columnIndex == l(cols)-1; } }); onTableChanged(table, voidfunc(final TableModelEvent e) { thread { O facts = dm_require_newFacts(); int i = e.getFirstRow(), j = e.getLastRow(); print(i + "/" + j); for (; i <= j; i++) { bool b = isTrue(print(rawCellData(table, i, l(cols)-1))); S id = (S) rawCellData(table, i, 0); call(facts, 'add, "#" + id + " is " + (b ? "a" : "not a") + " follow-up message"); call(facts, 'remove, "#" + id + " is " + (!b ? "a" : "not a") + " follow-up message"); } } }); ret table; } }