Libraryless. Click here for Pure Java version (25943L/171K).
1 | sbool SimpleCRUD_searcher = true; |
2 | |
3 | sclass SimpleCRUD<A extends Concept> { |
4 | Concepts concepts; |
5 | Class<A> cc; |
6 | JTable table; |
7 | JPanel buttons, panel; |
8 | O renderer; // optional, func(A) -> Map |
9 | O sorter; // optional, func(Cl<Concept>) -> Cl<Concept> |
10 | S hID = "ID"; |
11 | Set<S> unshownFields; // not shown in table or form |
12 | Set<S> excludeFieldsFromEditing; |
13 | S modifiedField; // field to hold last-modified timestamp |
14 | TableSearcher tableSearcher; |
15 | Set<S> multiLineFields; // string fields that should be shown as text areas |
16 | Set<S> dontDuplicateFields; |
17 | bool latestFirst; |
18 | int formFixer = 12; // stupid value to make submit button appear |
19 | bool editOnDoubleClick = true; |
20 | bool ensureIndexed; |
21 | bool showValuesAsStruct; // BREAKING CHANGE - used to be true |
22 | |
23 | *(Class<A> cc) { this(db_mainConcepts(), cc); } |
24 | *(Concepts *concepts, Class<A> *cc) {} |
25 | |
26 | SimpleCRUD<A> show(S frameTitle) { |
27 | make(); |
28 | showFrame(frameTitle, panel); |
29 | this; |
30 | } |
31 | |
32 | SimpleCRUD<A> show() { |
33 | ret show(plural(shortClassName(cc))); |
34 | } |
35 | |
36 | SimpleCRUD<A> showMaximized() { show(); maximizeFrame(panel); ret this; } |
37 | |
38 | JPanel makePanel() { ret make(); } |
39 | JPanel make() { |
40 | db(); |
41 | framesBot(); |
42 | ret make_dontStartBots(); |
43 | } |
44 | |
45 | JPanel make_dontStartBots() { |
46 | printVars("SimpleCRUD.make_dontStartBots", +concepts, +cc, count := countConcepts(cc)); |
47 | if (ensureIndexed) |
48 | indexConceptClass(concepts, cc); |
49 | |
50 | // next line not in swing part to allow passing arguments |
51 | // from outside like showConceptsTable_afterUpdate |
52 | temp tempSetTL(showConceptsTable_concepts, concepts); |
53 | temp tempSetTL(showConceptsTable_latestFirst, latestFirst); |
54 | temp tempSetTL(showConceptsTable_sorter, sorter); |
55 | temp tempSetTL(showConceptsTable_dropFields, asList(unshownFields)); |
56 | temp tempSetTL(dataToTable_useStruct, showValuesAsStruct); |
57 | table = makeConceptsTable(cc, wrapRenderer(renderer)); |
58 | swing { |
59 | buttons = jRightAlignedLine( |
60 | jbutton("Add...", r { newConcept() }), |
61 | tableDependButton(table, jbutton("Edit", r { |
62 | editConcept(selectedConcept()) |
63 | })), |
64 | tableDependButton(table, jbutton("Delete", r { |
65 | final L<A> l = selectedConcepts(); |
66 | thread "Delete concepts" { |
67 | withDBLock(concepts, r { for (A c : l) c.delete() }); |
68 | } |
69 | })), |
70 | tableDependButton(table, jbutton("Duplicate...", r { |
71 | duplicateConcept(selectedConcept()) |
72 | }))); |
73 | |
74 | if (SimpleCRUD_searcher) { |
75 | tableSearcher = tableWithSearcher2(table, withMargin := true); |
76 | panel = centerAndSouthWithMargin(tableSearcher.panel, withBottomMargin(buttons)); |
77 | } else |
78 | panel = centerAndSouthWithMargin(table, withBottomMargin(buttons)); |
79 | |
80 | O fEdit = voidfunc(int row) { editConcept(conceptForRow(row)) }; |
81 | tablePopupMenuItem(table, "Edit...", fEdit); |
82 | if (editOnDoubleClick) |
83 | onDoubleClick(table, fEdit); |
84 | } |
85 | ret panel; |
86 | } |
87 | |
88 | O wrapRenderer(fO renderer) { |
89 | ret renderer == null ? null : func(A a) { |
90 | putAll(litorderedmap(hID, str(a.id)), (Map) callF(renderer, a)) |
91 | }; |
92 | } |
93 | |
94 | void newConcept { |
95 | duplicateConcept(null); |
96 | } |
97 | |
98 | void duplicateConcept(A oldConcept) { |
99 | final A c = unlisted(cc); |
100 | ccopyFieldsExcept(oldConcept, c, dontDuplicateFields); |
101 | final Map<S, JComponent> map = makeComponents(c); |
102 | Runnable r = r { |
103 | concepts.register(c); |
104 | saveData(c, map); |
105 | }; |
106 | temp tempSetMCOpt(formLayouter1_fixer2 := formFixer); |
107 | showFormTitled2("New " + shortClassName(cc), arrayPlus(mapToObjectArray(map), r)); |
108 | } |
109 | |
110 | void editConcept(final A c) { |
111 | if (c == null) ret; |
112 | final Map<S, JComponent> map = makeComponents(c); |
113 | Runnable r = r { saveData(c, map) }; |
114 | temp tempSetMCOpt(formLayouter1_fixer2 := formFixer); |
115 | showFormTitled2("Edit " + shortClassName(cc) + " #" + c.id, arrayPlus(mapToObjectArray(map), r)); |
116 | } |
117 | |
118 | A selected aka selectedConcept() { |
119 | ret conceptForRow(selectedRow(table)); |
120 | } |
121 | |
122 | A conceptForRow(int row) { |
123 | ret (A) concepts.getConcept(toLong(getTableCell(table, row, 0))); |
124 | } |
125 | |
126 | int indexOfConcept(final A c) { |
127 | if (c == null) ret -1; |
128 | ret swing(func -> int { |
129 | int n = tableRowCount(table); |
130 | for row to n: |
131 | if (toLong(getTableCell(table, row, 0)) == c.id) |
132 | ret row; |
133 | ret -1; |
134 | }); |
135 | } |
136 | |
137 | L<A> selectedConcepts() { |
138 | ret swing(() -> { |
139 | int[] rows = table?.getSelectedRows(); |
140 | new L<A> l; |
141 | fOr (int row : rows) |
142 | l.add(conceptForRow(row)); |
143 | ret l; |
144 | }); |
145 | } |
146 | |
147 | Map<S, JComponent> makeComponents(A c) { |
148 | Map<S, JComponent> map = litorderedmap(); |
149 | makeComponents(c, map); |
150 | ret map; |
151 | } |
152 | |
153 | JComponent fieldComponent(A c, S field) { |
154 | Class type = getFieldType(cc, field); |
155 | O value = getOpt(c, field); |
156 | //print("Field type: " + field + " => " + type); |
157 | if (type == bool.class) |
158 | ret jCenteredCheckBox(isTrue(value)); |
159 | else if (contains(multiLineFields, field) || containsNewLines(optCast S(value))) |
160 | ret typeWriterTextArea((S) value); |
161 | else if (isSubtype(type, Concept)) |
162 | ret jcomboboxFromConcepts_str(concepts, type, (Concept) value); |
163 | ifclass SecretValue |
164 | else if (type == SecretValue.class) |
165 | ret jpassword(strOrEmpty(getVar(value/SecretValue))); |
166 | endif |
167 | else try { |
168 | ret autoComboBox(valueToString(value), new TreeSet<S>(map valueToString(collect(list(concepts, cc), field)))); |
169 | } catch e { |
170 | printException(e); |
171 | ret jTextField(valueToString(value)); |
172 | } |
173 | } |
174 | |
175 | void saveComponent(A c, S field, JComponent comp) { |
176 | comp = unwrap(comp); |
177 | Class type = fieldType(c, field); |
178 | ifclass SecretValue |
179 | if (type == SecretValue.class) { |
180 | S text = getTextTrim((JPasswordField) comp); |
181 | cset(c, field, empty(text) ? null : SecretValue(text)); |
182 | } else |
183 | endif |
184 | if (comp instanceof JTextComponent) |
185 | cset(c, field, convertToField(trimIf(!comp instanceof JTextArea, getText((JTextComponent) comp)), cc, field)); |
186 | else if (comp cast JComboBox) { |
187 | S text = getTextTrim(comp); |
188 | if (isSubtype(type, Concept)) |
189 | cset(c, field, getConcept(concepts, parseFirstLong(text))); |
190 | else |
191 | cset(c, field, convertToField(text, cc, field)); |
192 | } else if (comp instanceof JCheckBox) |
193 | cset(c, field, isChecked((JCheckBox) comp)); |
194 | ifclass ImageChooser |
195 | else if (comp instanceof ImageChooser) |
196 | cUpdatePNGFile(c, field, comp/ImageChooser.getImage(), false); |
197 | endif |
198 | } |
199 | |
200 | L<S> fields() { |
201 | if (excludeFieldsFromEditing != null && modifiedField != null) excludeFieldsFromEditing.add(modifiedField); |
202 | ret listWithoutSet([S field : conceptFieldsInOrder(cc) | fieldType(cc, field) != Concept.Ref.class], |
203 | joinSets(excludeFieldsFromEditing, unshownFields)); |
204 | } |
205 | |
206 | void excludeFieldsFromEditing(S... fields) { |
207 | excludeFieldsFromEditing = setPlus(excludeFieldsFromEditing, fields); |
208 | } |
209 | |
210 | // override the following two methods to customize edit window |
211 | |
212 | void makeComponents(A c, Map<S, JComponent> map) { |
213 | for (S field : fields()) |
214 | map.put(field, fieldComponent(c, field)); |
215 | } |
216 | |
217 | void saveData(A c, Map<S, JComponent> components) { |
218 | for (S field : keys(components)) |
219 | saveComponent(c, field, components.get(field)); |
220 | if (modifiedField != null) cset(c, modifiedField, now()); |
221 | } |
222 | |
223 | JTable table() { ret table; } |
224 | |
225 | swappable S valueToString(O o) { |
226 | ret showValuesAsStruct |
227 | ? structureOrText_crud(o) |
228 | : strOrNull(o); |
229 | } |
230 | } // end of SimpleCRUD |
Ref<PNGFile> fields can be saved, but are not shown automatically
download show line numbers debug dex old transpilations
Travelled to 16 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1006540 |
Snippet name: | SimpleCRUD - create/read/update/delete for a concept class with simple fields [Swing] |
Eternal ID of this version: | #1006540/99 |
Text MD5: | 8b2fd8c89a872f145d98fef41bfaea1b |
Transpilation MD5: | a5d4304b3c919484906283274b0b3454 |
Author: | stefan |
Category: | javax / concepts / gui |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-09-12 04:28:18 |
Source code size: | 7766 bytes / 230 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 1063 / 2284 |
Version history: | 98 change(s) |
Referenced in: | [show references] |