sbool SimpleCRUD_searcher = true;
sclass SimpleCRUD_v2 extends JConceptsTable {
JPanel buttons, panel;
S hID = "ID"; // Column header for concept ID
Set unshownFields; // not shown in table or form
Set excludeFieldsFromEditing;
S modifiedField; // field to hold last-modified timestamp
TableSearcher tableSearcher;
Set multiLineFields; // string fields that should be shown as text areas
Set dontDuplicateFields;
int formFixer = 12; // stupid value to make submit button appear
bool showBackRefs;
int maxRefsToShow = 3;
bool showClassNameSelectors;
bool allowNewFields;
int newFieldsToShow = 3;
*(Class conceptClass) { super(conceptClass); }
*(Concepts concepts, Class conceptClass) { super(concepts, conceptClass); }
SimpleCRUD_v2 show(S frameTitle) {
make();
showFrame(frameTitle, panel);
this;
}
SimpleCRUD_v2 show() {
ret show(plural(shortClassName(conceptClass)));
}
SimpleCRUD_v2 showMaximized() { show(); maximizeFrame(panel); ret this; }
JPanel makePanel() { ret make(); }
JPanel make() {
db();
framesBot();
ret make_dontStartBots();
}
swappable MapSO itemToMap_inner(A a) {
ret super.itemToMap_base(a);
}
MapSO itemToMap_base(A a) {
MapSO map = itemToMap_inner(a);
if (map == null) null;
ret putAll(putAll(specialFieldsForItem(a), map), moreSpecialFieldsForItem(a));
}
// 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;
}
// shown on the right (usually)
swappable MapSO moreSpecialFieldsForItem(A a) {
MapSO map = litorderedmap();
if (showBackRefs) {
Cl refs = allBackRefs(a);
if (nempty(refs)) {
refs = sortedByConceptID(refs);
int more = l(refs)-maxRefsToShow;
map.put("Referenced by",
joinWithComma(takeFirst(maxRefsToShow, refs))
+ (more > 0 ? ", " + more + " more" : ""));
}
}
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;
}
JPanel make_dontStartBots() {
dropFields = asList(unshownFields);
makeTable();
swing {
buttons = jRightAlignedLine(
jbutton("Add...", r { newConcept() }),
tableDependButton(table, jbutton("Edit", r {
editConcept(selectedConcept())
})),
tableDependButton(table, jbutton("Delete", r {
final L l = selectedConcepts();
withDBLock(concepts, r { for (A c : l) c.delete() });
})),
tableDependButton(table, jbutton("Duplicate...", r {
duplicateConcept(selectedConcept())
})));
if (SimpleCRUD_searcher) {
tableSearcher = tableWithSearcher2(table, withMargin := true);
panel = centerAndSouthWithMargin(tableSearcher.panel, buttons);
} else
panel = centerAndSouthWithMargin(table, buttons);
O fEdit = voidfunc(int row) {
editConcept(getItem(row))
};
tablePopupMenuItem(table, "Edit...", fEdit);
onDoubleClick(table, fEdit);
tablePopupMenuFirst(table, (menu, row) -> {
Concept c = getItem(row);
if (c != null)
addMenuItem(menu, "Delete " + quote(shorten(str(c))), rThread {
deleteConcept(c);
});
});
} // end of swing
ret panel;
}
void newConcept {
duplicateConcept(null);
}
void duplicateConcept(A oldConcept) {
new EditWindow ew;
ew.item = (A) unlisted(oldConcept == null ? conceptClass : _getClass(oldConcept));
ccopyFieldsExcept(oldConcept, ew.item, dontDuplicateFields);
makeComponents(ew);
Runnable r = r {
concepts.register(ew.item);
saveData(ew);
};
temp tempSetMCOpt(formLayouter1_fixer2 := formFixer);
showFormTitled2("New " + shortClassName(conceptClass),
arrayPlus(mapToObjectArray(ew.componentsByField), r));
}
void editConcept(A c) {
if (c == null) ret;
new EditWindow ew;
ew.item = c;
makeComponents(ew);
Runnable r = r { saveData(ew) };
temp tempSetMCOpt(formLayouter1_fixer2 := formFixer);
showFormTitled2("Edit " + shortClassName(conceptClass) + " #" + c.id, arrayPlus(mapToObjectArray(ew.componentsByField), r));
}
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;
}
JComponent fieldComponent(A c, S field) {
Class type = getFieldType(conceptClass, field);
O value = getOpt(c, field);
//print("Field type: " + field + " => " + type);
if (type == bool.class)
ret jCenteredCheckBox(isTrue(value));
else if (contains(multiLineFields, field) || containsNewLines(optCast S(value)))
ret typeWriterTextArea((S) value);
else if (isSubtype(type, Concept))
ret jcomboboxFromConcepts_str(concepts, type, (Concept) value);
ifclass SecretValue
else if (type == SecretValue.class)
ret jpassword(strOrEmpty(getVar(value/SecretValue)));
endif
else try {
ret autoComboBox(structureOrText_crud(value), new TreeSet(map structureOrText_crud(collect(list(concepts, conceptClass), field))));
} catch e {
printException(e);
ret jTextField(structureOrText_crud(value));
}
}
void saveComponent(A c, S field, JComponent comp) {
comp = unwrap(comp);
Class type = fieldType(c, field);
ifclass SecretValue
if (type == SecretValue.class) {
S text = getTextTrim((JPasswordField) comp);
cset(c, field, empty(text) ? null : SecretValue(text));
} else
endif
if (comp instanceof JTextComponent)
cset(c, field, convertToField(trimIf(!comp instanceof JTextArea, getText((JTextComponent) comp)), conceptClass, field));
else if (comp cast JComboBox) {
S text = getTextTrim(comp);
if (isSubtype(type, Concept))
cset(c, field, getConcept(concepts, parseFirstLong(text)));
else
cset(c, field, convertToField(text, conceptClass, field));
} else if (comp instanceof JCheckBox)
cset(c, field, isChecked((JCheckBox) comp));
ifclass ImageChooser
else if (comp instanceof ImageChooser)
cUpdatePNGFile(c, field, comp/ImageChooser.getImage(), false);
endif
}
L fieldsForItem(A c) {
if (excludeFieldsFromEditing != null && modifiedField != null) excludeFieldsFromEditing.add(modifiedField);
ret listWithoutSet([S field : conceptFields(c) | fieldType(conceptClass, field) != Concept.Ref.class],
joinSets(excludeFieldsFromEditing, unshownFields));
}
void excludeFieldsFromEditing(S... fields) {
excludeFieldsFromEditing = setPlus(excludeFieldsFromEditing, fields);
}
Cl> possibleClasses() {
ret (Cl) moveItemFirst(conceptClass, dropTypeParameter(sortClassesByNameIC(myNonAbstractClassesImplementing(conceptClass))));
}
JComboBox> classSelectorComponent(A c) {
ret setComboBoxRenderer(jTypedComboBox(possibleClasses(), _getClass(c)),
customToStringListCellRenderer shortClassName());
}
class NewField {
JTextField tfName = jtextfield();
JTextField tfValue = jtextfield();
JPanel panel() {
ret jhgridWithSpacing(withLabel("Name", tfName),
withLabel("Value", tfValue));
}
S field() { ret gtt(tfName); }
O value() { ret gtt(tfValue); }
}
class EditWindow {
A item;
Map componentsByField = litorderedmap();
JComboBox> classSelector;
new L newFields;
}
// override the following two methods to customize edit window
void makeComponents(EditWindow ew) {
// class selector
if (showClassNameSelectors)
ew.componentsByField.put("Java Class", ew.classSelector = classSelectorComponent(ew.item));
// regular fields
for (S field : fieldsForItem(ew.item))
ew.componentsByField.put(field, fieldComponent(ew.item, field));
// new fields
if (allowNewFields) {
for i to newFieldsToShow: {
new NewField nf;
ew.newFields.add(nf);
ew.componentsByField.put("New field " + (i+1), nf.panel());
}
}
}
void saveData(EditWindow ew) {
// save regular fields
for (S field, JComponent component : ew.componentsByField)
if (isIdentifier(field))
saveComponent(ew.item, field, component);
// save new fields
for (NewField nf : ew.newFields) {
S field = nf.field();
O value = nf.value();
if (nempty(field) && notNullOrEmptyString(value))
cset(ew.item, field, value);
}
if (modifiedField != null) cset(ew.item, modifiedField, now());
}
}