!7 sclass Msg { S author, text; long created = now(); } concept ItemPage { S globalID = aGlobalID(); S title, imageID; long created = now(), occurred; L comments = listInConcept(this); } /*concept ItemPageAsList { new Ref item; L items = listInConcept(this); }*/ static JTextPane textPane; static JTextField tfComment; static ItemPage page; static JButton btnBack, btnNext; p-subst { bootstrapConceptsFrom(#1009748); setConsoleInputFontSize(20); centerConsoleInput(); centerBottomConsole(); db(); bot("Show Item Page."); focusConsole(); awtOnConceptChanges(consoleFrame(), r { consoleTitle(n(countConcepts(ItemPage), "Item Page")) }); } static bool showItemPage(S _word) { final new ItemPage p; new Matches m; if (match("user said *", _word, m)) _word = format("You said: *", $1); fS word = trim(_word); cset(p, title := word); showPage(p); focusConsole(); true; } svoid showPage(final ItemPage p) { if (p == null) ret; swing { page = p; fS shorter = deUserSaid(p.title); JFrame f = getFrame(textPane); fS text = p.title + "\n\n"; L l = list(ItemPage); int idx = l.indexOf(page); S frameTitle = shorter + " [" + (idx+1) + "/" + l(l) + "]"; JTextPane oldTextPane = textPane; textPane = jcenteredtextpane(text, style_bold(style_fontSize(24))); if (oldTextPane != null) { replaceAWTComponent(oldTextPane, textPane); setFrameTitle(frameTitle, textPane); frameToFront(f); } else { showFrame(frameTitle, textPane); addToFrame(textPane, withLabel("Add Comment:", centerAndEast(tfComment = jtextfield(), jbutton("OK", f addComment)))); addToFrameNorth(textPane, jcenteredline( btnBack = jbutton("<", r { step(-1) }), btnNext = jbutton(">", r { step(1) }))); onEnter(tfComment, f addComment); } setEnabled(btnBack, idx > 0); setEnabled(btnNext, idx < l(l)-1); textPane.setEditable(false); if (p.imageID == null) thread { BufferedImage img = quickVisualize(shorter); if (p.imageID != null) ret; cset(p, imageID := "quickvis/" + urlencode(quickVisualize_preprocess(shorter)) + "/" + md5OfBufferedImage(img)); showPage(p); } else { BufferedImage img = quickVisualize(shorter); addImageToTextPane(textPane, img); // sync OK, uses swing {} } for (Msg c : p.comments) appendToTextPane(textPane, "\n\n" + c.author + ": " + c.text, style_normal()); centerTextPane(textPane); } } answer { if (eq(s, "!list")) { showPagesList(); ret "OK"; } if "quickrun *" { fS prog = $1; thread { _run($1); print("[done]"); } ret "OK"; } if (showItemPage(s)) ret "OK"; } svoid addComment { S text = getTextTrimAndClear(tfComment); if (empty(text)) ret; page.comments.add(nuWithValues(Msg, author := 'User, +text)); showPage(page); } // called by child processes svoid addProgramComment(S programID, ItemPage page, S text) { page.comments.add(nuWithValues(Msg, author := programID, +text)); showPage(page); } svoid showPagesList { onDoubleClick(showList_updatedOnConceptsChange("Item Pages", func { map(list(ItemPage), func(ItemPage p) { p.globalID + " - " + p.title })}), voidfunc(S s) { showPage(findConcept(ItemPage, globalID := dropAfterSpace(s))) }); } svoid step(int delta) { L l = list(ItemPage); showPage(get(l, l.indexOf(page)+delta)); }