1 | sclass SimpleCRUD_v2<A extends Concept> extends JConceptsTable<A> { |
2 | JPanel buttons, panel; |
3 | Set<S> unshownFields; // not shown in table or form |
4 | Set<S> excludeFieldsFromEditing; |
5 | S modifiedField; // field to hold last-modified timestamp |
6 | TableSearcher tableSearcher; |
7 | Set<S> multiLineFields; // string fields that should be shown as text areas |
8 | Set<S> dontDuplicateFields; |
9 | int formFixer = 12; // stupid value to make submit button appear |
10 | bool showBackRefs; |
11 | int maxRefsToShow = 3; |
12 | bool showClassNameSelectors; |
13 | bool allowNewFields; |
14 | int newFieldsToShow = 3; |
15 | bool emptyStringsToNull; // convert all empty string values to nulls (also means dropping the field if it's dynamic) |
16 | int formLabelsWidth = 100; // just another bug fixer |
17 | settable bool showSearchBar = true; |
18 | settable bool showAddButton = true; |
19 | settable bool showEditButton = true; |
20 | |
21 | *(Class<A> conceptClass) { super(conceptClass); } |
22 | *(Concepts concepts, Class<A> conceptClass) { super(concepts, conceptClass); } |
23 | |
24 | SimpleCRUD_v2<A> show(S frameTitle) { |
25 | make(); |
26 | showFrame(frameTitle, panel); |
27 | this; |
28 | } |
29 | |
30 | SimpleCRUD_v2<A> show() { |
31 | ret show(plural(shortClassName(conceptClass))); |
32 | } |
33 | |
34 | SimpleCRUD_v2<A> showMaximized() { show(); maximizeFrame(panel); ret this; } |
35 | |
36 | JPanel makePanel() { ret make(); } |
37 | JPanel make() { |
38 | db(); |
39 | framesBot(); |
40 | ret make_dontStartBots(); |
41 | } |
42 | |
43 | swappable MapSO itemToMap_inner(A a) { |
44 | ret super.itemToMap_base(a); |
45 | } |
46 | |
47 | MapSO itemToMap_base(A a) { |
48 | MapSO map = itemToMap_inner(a); |
49 | if (map == null) null; |
50 | ret putAll(map, moreSpecialFieldsForItem(a)); |
51 | } |
52 | |
53 | // shown on the right (usually) |
54 | swappable MapSO moreSpecialFieldsForItem(A a) { |
55 | MapSO map = litorderedmap(); |
56 | if (showBackRefs) { |
57 | Cl<Concept> refs = allBackRefs(a); |
58 | if (nempty(refs)) { |
59 | refs = sortedByConceptID(refs); |
60 | int more = l(refs)-maxRefsToShow; |
61 | map.put("Referenced by", |
62 | joinWithComma(takeFirst(maxRefsToShow, refs)) |
63 | + (more > 0 ? ", " + more + " more" : "")); |
64 | } |
65 | } |
66 | ret map; |
67 | } |
68 | |
69 | JPanel make_dontStartBots() { |
70 | dropFields = asList(unshownFields); |
71 | makeTable(); |
72 | swing { |
73 | buttons = jRightAlignedLine(flattenToList( |
74 | !showAddButton ? null : jbutton("Add...", r { newConcept() }), |
75 | !showEditButton ? null : tableDependButton(table, jbutton("Edit", r { |
76 | editConcept(selectedConcept()) |
77 | })), |
78 | !showEditButton ? null : tableDependButton(table, jbutton("Duplicate...", r { |
79 | duplicateConcept(selectedConcept()) |
80 | })), |
81 | tableDependButton(table, jbutton("Delete", r { |
82 | final L<A> l = selectedConcepts(); |
83 | withDBLock(concepts, r { for (A c : l) c.delete() }); |
84 | })), |
85 | )); |
86 | |
87 | if (showSearchBar) { |
88 | tableSearcher = tableWithSearcher2(table, withMargin := true); |
89 | panel = centerAndSouthWithMargin(tableSearcher.panel, buttons); |
90 | } else |
91 | panel = centerAndSouthWithMargin(table, buttons); |
92 | |
93 | O fEdit = voidfunc(int row) { |
94 | editConcept(getItem(row)) |
95 | }; |
96 | tablePopupMenuItem(table, "Edit...", fEdit); |
97 | onDoubleClick(table, fEdit); |
98 | tablePopupMenuFirst(table, (menu, row) -> { |
99 | Concept c = getItem(row); |
100 | if (c != null) |
101 | addMenuItem(menu, "Delete " + quote(shorten(str(c))), rThread { |
102 | deleteConcept(c); |
103 | }); |
104 | }); |
105 | } // end of swing |
106 | ret panel; |
107 | } |
108 | |
109 | void newConcept { |
110 | duplicateConcept(null); |
111 | } |
112 | |
113 | void duplicateConcept(A oldConcept) { |
114 | new EditWindow ew; |
115 | ew.item = (A) unlisted(oldConcept == null ? conceptClass : _getClass(oldConcept)); |
116 | ccopyFieldsExcept(oldConcept, ew.item, dontDuplicateFields); |
117 | makeComponents(ew); |
118 | |
119 | // save action |
120 | F0<Bool> r = func -> bool { |
121 | try { |
122 | concepts.register(ew.item); |
123 | saveData(ew); |
124 | setSelected(ew.item); |
125 | } catch print e { |
126 | infoBox(e); |
127 | false; |
128 | } |
129 | true; |
130 | }; |
131 | temp tempSetMCOpt(formLayouter1_fixer2 := formFixer); |
132 | renameSubmitButton("Create", showAForm("New " + shortClassName(conceptClass), |
133 | toObjectArray(listPlus(ew.matrix, r)))); |
134 | } |
135 | |
136 | void editConcept(A c) { |
137 | if (c == null) ret; |
138 | new EditWindow ew; |
139 | ew.item = c; |
140 | makeComponents(ew); |
141 | F0<Bool> r = func -> bool { |
142 | try { |
143 | // concept class was changed, replace object |
144 | if (ew.item != c) { |
145 | print("Replacing object: " + c + " => " + ew.item); |
146 | replaceConceptAndUpdateRefs(c, ew.item); |
147 | } |
148 | saveData(ew); |
149 | } catch print e { |
150 | infoBox(e); |
151 | false; |
152 | } |
153 | true; |
154 | }; |
155 | temp tempSetMCOpt(formLayouter1_fixer2 := formFixer); |
156 | renameSubmitButton("Save", showAForm("Edit " + shortClassName(conceptClass) + " #" + c.id, |
157 | toObjectArray(listPlus(ew.matrix, r)))); |
158 | } |
159 | |
160 | JComponent fieldComponent(A c, S field) { |
161 | Class type = getFieldType(conceptClass, field); |
162 | O value = cget(c, field); |
163 | if (type == null) type = _getClass(value); |
164 | print("Field type: " + field + " => " + type); |
165 | if (type == bool.class) |
166 | ret jCenteredCheckBox(isTrue(value)); |
167 | else if (contains(multiLineFields, field) || containsNewLines(optCast S(value))) |
168 | ret typeWriterTextArea((S) value); |
169 | else if (isSubtype(type, Concept)) |
170 | ret jcomboboxFromConcepts_str(concepts, type, (Concept) value); |
171 | ifclass SecretValue |
172 | else if (type == SecretValue.class) |
173 | ret jpassword(strOrEmpty(getVar(value/SecretValue))); |
174 | endif |
175 | else if (isUneditableFieldType(type)) |
176 | ret jlabel(structureOrText_crud(value)); |
177 | else try { |
178 | ret autoComboBox(structureOrText_crud(value), new TreeSet<S>(map structureOrText_crud(collect(list(concepts, conceptClass), field)))); |
179 | } catch e { |
180 | printException(e); |
181 | ret jTextField(structureOrText_crud(value)); |
182 | } |
183 | } |
184 | |
185 | void saveComponent(A c, S field, JComponent comp) { |
186 | comp = unwrap(comp); |
187 | ifdef SimpleCRUD_v2_debug |
188 | printVars_str("saveComponent", +field, +comp); |
189 | endifdef |
190 | Class type = fieldType(c, field); |
191 | ifclass SecretValue |
192 | if (type == SecretValue.class) { |
193 | S text = getTextTrim((JPasswordField) comp); |
194 | cset(c, field, empty(text) ? null : SecretValue(text)); |
195 | } else |
196 | endif |
197 | if (comp instanceof JTextComponent) { |
198 | S text = trimIf(!comp instanceof JTextArea, getText((JTextComponent) comp)); |
199 | O value = postProcessValue(text); |
200 | O converted = convertToField(value, conceptClass, field); |
201 | ifdef SimpleCRUD_v2_debug |
202 | printVars_str("saveComponent", +field, +text, +value, +converted); |
203 | endifdef |
204 | cset(c, field, converted); |
205 | } |
206 | else if (comp cast JComboBox) { |
207 | S text = getTextTrim(comp); |
208 | if (isSubtype(type, Concept)) |
209 | cset(c, field, getConcept(concepts, parseFirstLong(text))); |
210 | else { |
211 | O value = postProcessValue(text); |
212 | cset(c, field, convertToField(value, conceptClass, field)); |
213 | } |
214 | } else if (comp instanceof JCheckBox) |
215 | cset(c, field, isChecked((JCheckBox) comp)); |
216 | ifclass ImageChooser |
217 | else if (comp instanceof ImageChooser) |
218 | cUpdatePNGFile(c, field, comp/ImageChooser.getImage(), false); |
219 | endif |
220 | } |
221 | |
222 | L<S> fieldsForItem(A c) { |
223 | if (excludeFieldsFromEditing != null && modifiedField != null) excludeFieldsFromEditing.add(modifiedField); |
224 | ret listWithoutSet([S field : conceptFields(c) | fieldType(conceptClass, field) != Concept.Ref.class], |
225 | joinSets(excludeFieldsFromEditing, unshownFields)); |
226 | } |
227 | |
228 | void excludeFieldsFromEditing(S... fields) { |
229 | excludeFieldsFromEditing = setPlus(excludeFieldsFromEditing, fields); |
230 | } |
231 | |
232 | |
233 | Cl<Class<? extends A>> possibleClasses() { |
234 | ret (Cl) moveItemFirst(conceptClass, dropTypeParameter(sortClassesByNameIC(myNonAbstractClassesImplementing(conceptClass)))); |
235 | } |
236 | |
237 | JComboBox<Class<? extends A>> classSelectorComponent(A c) { |
238 | ret setComboBoxRenderer(jTypedComboBox(possibleClasses(), _getClass(c)), |
239 | customToStringListCellRenderer shortClassName()); |
240 | } |
241 | |
242 | class NewField { |
243 | JTextField tfName = jtextfield(); |
244 | JTextField tfValue = jtextfield(); |
245 | JComboBox cbRef = jcomboboxFromConcepts_str(concepts, conceptClass); |
246 | JComboBox cbType = jcombobox("String", "Reference"); |
247 | SingleComponentPanel scpValue = singleComponentPanel(); |
248 | |
249 | JPanel panel() { |
250 | onChangeAndNow(cbType, r updateSCP); |
251 | ret jhgridWithSpacing( |
252 | withToolTip("Name for new field", withLabel("Name", tfName)), |
253 | withLabel("Value", westAndCenterWithMargin(cbType, scpValue))); |
254 | } |
255 | |
256 | S typeStr() { ret getText(cbType); } |
257 | |
258 | void updateSCP { |
259 | scpValue.setComponent(eqic(typeStr(), "Reference") |
260 | ? cbRef |
261 | : withToolTip("Contents of new field", tfValue)); |
262 | } |
263 | |
264 | S field() { ret gtt(tfName); } |
265 | O value() { |
266 | ret eqic(typeStr(), "Reference") |
267 | ? getConcept(concepts, parseFirstLong(getText(cbRef))) |
268 | : gtt(tfValue); |
269 | } |
270 | } |
271 | |
272 | class EditWindow { |
273 | A item; |
274 | Map<S, JComponent> componentsByField = litorderedmap(); |
275 | new L matrix; // label, component, label, component, ... |
276 | JComboBox<Class<? extends A>> classSelector; |
277 | new L<NewField> newFields; |
278 | } |
279 | |
280 | // override the following two methods to customize edit window |
281 | |
282 | void makeComponents(EditWindow ew) { |
283 | // class selector |
284 | |
285 | if (showClassNameSelectors) { |
286 | addAll(ew.matrix, makeLabel("Java Class"), ew.classSelector = classSelectorComponent(ew.item)); |
287 | onChange(ew.classSelector, r { |
288 | Class<? extends A> oldClass = _getClass(ew.item); |
289 | Class<? extends A> newClass = getSelectedItem_typed(ew.classSelector); |
290 | if (oldClass == newClass) ret; |
291 | A oldItem = ew.item; |
292 | ew.item = unlisted(newClass); |
293 | ccopyFields(oldItem, ew.item); |
294 | }); |
295 | } |
296 | |
297 | // regular fields |
298 | |
299 | for (S field : fieldsForItem(ew.item)) { |
300 | JComponent c = fieldComponent(ew.item, field); |
301 | ew.componentsByField.put(field, c); |
302 | addAll(ew.matrix, makeLabel(field), c); |
303 | } |
304 | |
305 | // new fields |
306 | |
307 | if (allowNewFields && newFieldsToShow > 0) { |
308 | addAll(ew.matrix, " ", jlabel()); // spacing |
309 | for i to newFieldsToShow: { |
310 | new NewField nf; |
311 | ew.newFields.add(nf); |
312 | addAll(ew.matrix, makeLabel(""/*"New field"*/), nf.panel()); |
313 | } |
314 | } |
315 | } |
316 | |
317 | void saveData(EditWindow ew) { |
318 | // save regular fields |
319 | |
320 | for (S field, JComponent component : ew.componentsByField) |
321 | if (isIdentifier(field)) |
322 | saveComponent(ew.item, field, component); |
323 | |
324 | // save new fields |
325 | |
326 | for (NewField nf : ew.newFields) { |
327 | S field = nf.field(); |
328 | O value = nf.value(); |
329 | if (nempty(field) && notNullOrEmptyString(value)) |
330 | cset(ew.item, field, value); |
331 | } |
332 | |
333 | if (modifiedField != null) cset(ew.item, modifiedField, now()); |
334 | } |
335 | |
336 | O postProcessValue(O o) { |
337 | if (emptyStringsToNull && eq(o, "")) null; |
338 | ret o; |
339 | } |
340 | |
341 | // labels on left hand side of form |
342 | JComponent makeLabel(S label) { |
343 | ret jMinWidthAtLeast(formLabelsWidth, jlabel(label)); |
344 | } |
345 | |
346 | swappable JComponent showAForm(S title, O... parts) { |
347 | ret showFormTitled2(title, parts); |
348 | } |
349 | |
350 | bool isUneditableFieldType(Class type) { |
351 | ret isSubclassOfAny(type, Map, L, Pair); |
352 | } |
353 | |
354 | void hideFields(S... fields) { |
355 | unshownFields = createOrAddToSet(unshownFields, fields); |
356 | } |
357 | } // end of SimpleCRUD_v2 |
Began life as a copy of #1030726
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1033931 |
Snippet name: | SimpleCRUD_v2 - create/read/update/delete for a concept class with simple fields [Swing, backup] |
Eternal ID of this version: | #1033931/4 |
Text MD5: | 91e388e103c98db3b74f811391d065c9 |
Author: | stefan |
Category: | javax / concepts / gui |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-02-10 08:42:04 |
Source code size: | 11832 bytes / 357 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 113 / 133 |
Version history: | 3 change(s) |
Referenced in: | [show references] |