Libraryless. Click here for Pure Java version (30096L/193K).
1 | sclass JConceptsTable<A extends Concept> is Swingable { |
2 | Class<? extends A> conceptClass; |
3 | Concepts concepts; |
4 | JTable table; |
5 | |
6 | // options |
7 | MapSO filters; // fields to filter by/add to new objects |
8 | S hID = "ID"; // Column header for concept ID |
9 | settable LS dropFields; |
10 | settable bool noSubclasses; |
11 | IF1<L> postProcess; |
12 | Runnable afterUpdate; |
13 | bool latestFirst; |
14 | IF1<Cl<A>> sorter = lambda1 defaultSort; |
15 | int idWidth = 50; |
16 | settable int updateInterval = 100; |
17 | int firstUpdateInterval = 100; |
18 | bool humanizeFieldNames = true; |
19 | Float tableFontSize; |
20 | Int tableRowHeight; |
21 | settable bool addCountToEnclosingTab; |
22 | settable bool useNewChangeHandler; |
23 | settable IVF1<A> defaultAction; |
24 | bool pauseUpdates; |
25 | int count; |
26 | |
27 | event selectionChanged; |
28 | event singleSelectionChanged; |
29 | |
30 | // internal |
31 | AWTOnConceptChanges changeHandler; |
32 | AWTOnConceptChangesByClass newChangeHandler; |
33 | bool updatingList; |
34 | settable A selectAfterUpdate; |
35 | A lastSelected; |
36 | |
37 | *() {} |
38 | *(Class<? extends A> *conceptClass) {} |
39 | *(Concepts *concepts, Class<? extends A> *conceptClass) {} |
40 | |
41 | swappable MapSO itemToMap(A a) { |
42 | MapSO map = specialFieldsForItem(a); |
43 | try { |
44 | putAll(map, mapValues renderValue(itemToMap_inner2(a))); |
45 | } catch print e { |
46 | map.put("Error", str(e)); |
47 | } |
48 | ret map; |
49 | } |
50 | |
51 | swappable O renderValue(O o) { |
52 | ret renderForTable_noStruct(o); |
53 | } |
54 | |
55 | swappable MapSO itemToMap_inner2(A a) { |
56 | ret allConceptFieldsAsMapExcept(a, dropFields); |
57 | } |
58 | |
59 | // shown on the left (usually) |
60 | swappable MapSO specialFieldsForItem(A a) { |
61 | MapSO map = litorderedmap(hID, str(a.id)); |
62 | mapPut(map, "Java Class", javaClassDescForItem(a)); |
63 | ret map; |
64 | } |
65 | |
66 | S javaClassDescForItem(A a) { |
67 | S className = dynShortClassName(a); |
68 | if (neq(className, shortClassName(conceptClass))) { |
69 | S text = className; |
70 | S realClass = shortClassName(a); |
71 | if (neq(className, realClass)) |
72 | text += " as " + realClass; |
73 | ret text; |
74 | } |
75 | null; |
76 | } |
77 | |
78 | S defaultTitle() { |
79 | ret plural(shortClassName(conceptClass)); |
80 | } |
81 | |
82 | void showAsFrame(S title default defaultTitle()) { |
83 | makeTable(); |
84 | showFrame(title, table); |
85 | } |
86 | |
87 | void makeTable { |
88 | if (table != null) ret; |
89 | if (concepts == null) concepts = db_mainConcepts(); |
90 | |
91 | table = sexyTable(); |
92 | if (tableFontSize != null) { |
93 | setTableFontSizes(tableFontSize, table); |
94 | if (tableRowHeight == null) |
95 | tableRowHeight = iround(tableFontSize*1.5); |
96 | } |
97 | if (tableRowHeight != null) setRowHeight(table, tableRowHeight); |
98 | |
99 | if (useNewChangeHandler) { |
100 | newChangeHandler = new AWTOnConceptChangesByClass(concepts, conceptClass, table, l0 _update) |
101 | .delay(updateInterval) |
102 | .firstDelay(firstUpdateInterval); |
103 | newChangeHandler.install(); |
104 | } else { |
105 | changeHandler = new AWTOnConceptChanges(concepts, table, l0 _update) |
106 | .delay(updateInterval) |
107 | .firstDelay(firstUpdateInterval); |
108 | changeHandler.install(); |
109 | } |
110 | |
111 | onTableSelectionChanged(table, -> { |
112 | if (updatingList) ret; |
113 | selectionChanged(); |
114 | }); |
115 | |
116 | onSelectionChanged(-> { |
117 | var a = selected(); |
118 | if (a != lastSelected) { |
119 | lastSelected = a; |
120 | singleSelectionChanged(); |
121 | } |
122 | }); |
123 | |
124 | onDoubleClickOrEnter(table, -> { |
125 | A a = selected(); |
126 | if (a != null && defaultAction != null) |
127 | pcallF(defaultAction, a); |
128 | }); |
129 | } |
130 | |
131 | // e.g. to update enclosing tab when hidden |
132 | void update { |
133 | swing { _update(); } |
134 | } |
135 | |
136 | // run in Swing thread |
137 | void _update { |
138 | if (table == null || pauseUpdates) ret; |
139 | set updatingList; |
140 | bool allRestored; |
141 | A selectAfterUpdate = selectAfterUpdate(); |
142 | |
143 | try { |
144 | new L<Map> data; |
145 | |
146 | Set<Long> selection; |
147 | if (selectAfterUpdate != null) { |
148 | selection = litset(selectAfterUpdate._conceptID()); |
149 | selectAfterUpdate(null); |
150 | } else |
151 | selection = toSet(selectedConceptIDs()); |
152 | |
153 | Cl<? extends A> l = conceptsWhere(concepts, conceptClass, mapToParams(filters)); |
154 | if (noSubclasses) l = filter(l, x -> x.getClass() == conceptClass); |
155 | l = postProcess(sorter, l); |
156 | for (A c : l) |
157 | addIfNotNull(data, itemToMap(c)); |
158 | if (latestFirst) reverseInPlace(data); |
159 | data = (L) postProcess(postProcess, data); |
160 | count = l(data); |
161 | dataToTable_uneditable(data, table); |
162 | if (humanizeFieldNames) |
163 | humanizeTableColumns(); |
164 | tableColumnMaxWidth(table, 0, idWidth); |
165 | |
166 | allRestored = restoreSelection(selection); |
167 | |
168 | if (addCountToEnclosingTab) |
169 | updateEnclosingTabTitle(); |
170 | } finally { |
171 | updatingList = false; |
172 | } |
173 | |
174 | pcallF(afterUpdate); |
175 | |
176 | if (!allRestored || selectAfterUpdate != null) |
177 | selectionChanged(); |
178 | } |
179 | |
180 | void updateEnclosingTabTitle { |
181 | updateEnclosingTabTitleWithCount(table, count); |
182 | } |
183 | |
184 | void humanizeTableColumns { |
185 | int n = tableColumnCount(table); |
186 | for i to n: |
187 | setColumnName(table, i, humanizeFormLabel(getColumnName(table, i))); |
188 | }; |
189 | |
190 | visual table(); |
191 | |
192 | JTable table() { |
193 | makeTable(); |
194 | ret table; |
195 | } |
196 | |
197 | A selectedConcept() { |
198 | ret (A) concepts.getConcept(toLong(selectedTableCell(table, 0))); |
199 | } |
200 | |
201 | A selected() { ret selectedConcept(); } |
202 | |
203 | long getItemID(int row) { |
204 | ret toLong(getTableCell(table, row, 0)); |
205 | } |
206 | |
207 | L<A> getList() swing { |
208 | ret countIteratorAsList(size(), row -> getItem(row)); |
209 | } |
210 | |
211 | A getItem(int row) { |
212 | ret (A) concepts.getConcept(getItemID(row)); |
213 | } |
214 | |
215 | int size() { ret tableRowCount(table); } |
216 | |
217 | int indexOfConcept(final A c) { |
218 | if (c == null) ret -1; |
219 | ret swing(func -> int { |
220 | int n = size(); |
221 | for row to n: |
222 | if (toLong(getTableCell(table, row, 0)) == c.id) |
223 | ret row; |
224 | ret -1; |
225 | }); |
226 | } |
227 | |
228 | L<A> selectedConcepts() { |
229 | ret swing(-> { |
230 | int[] rows = table.getSelectedRows(); |
231 | new L<A> l; |
232 | for (int row : rows) |
233 | l.add(getItem(row)); |
234 | ret l; |
235 | }); |
236 | } |
237 | |
238 | L<Long> selectedConceptIDs() { |
239 | ret swing(-> { |
240 | int[] rows = table.getSelectedRows(); |
241 | L<Long> l = emptyList(l(rows)); |
242 | for (int row : rows) |
243 | l.add(getItemID(row)); |
244 | ret l; |
245 | }); |
246 | } |
247 | |
248 | // returns true if all selected items still exist |
249 | bool restoreSelection(Set<Long> selection) { |
250 | ret swing(-> { |
251 | int n = size(); |
252 | new IntBuffer toSelect; |
253 | for row to n: |
254 | if (selection.contains(getItemID(row))) |
255 | toSelect.add(row); |
256 | selectTableRows(table, toSelect.toIntArray()); |
257 | ret toSelect.size() == selection.size(); |
258 | }); |
259 | } |
260 | |
261 | void setSelected(A a) { |
262 | selectRow(table(), indexOfConcept(a)); |
263 | } |
264 | |
265 | Cl<A> defaultSort(Cl<A> l) { |
266 | ret sortByConceptID(l); |
267 | } |
268 | |
269 | selfType addFilter(S field, O value) { |
270 | filters = orderedMapPutOrCreate(filters, field, value); |
271 | this; |
272 | } |
273 | |
274 | void onSelectionChangedAndWhenShowing(Runnable r) { |
275 | bindToComponent(table(), r); |
276 | onSelectionChanged(r); |
277 | } |
278 | |
279 | void onSelectionChangedAndNow(Runnable r) { |
280 | if (r == null) ret; |
281 | onSelectionChanged(r); |
282 | r.run(); |
283 | } |
284 | |
285 | selfType updateInterval(double seconds) { |
286 | ret updateInterval(toMS_int(seconds)); |
287 | } |
288 | |
289 | selfType pauseUpdates(bool b) { |
290 | swing { |
291 | if (pauseUpdates != b) { |
292 | if (!(pauseUpdates = b)) |
293 | _update(); |
294 | } |
295 | } |
296 | this; |
297 | } |
298 | } |
Began life as a copy of #1006009
download show line numbers debug dex old transpilations
Travelled to 5 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt
No comments. add comment
Snippet ID: | #1030725 |
Snippet name: | JConceptsTable - new version of showConceptsTable |
Eternal ID of this version: | #1030725/76 |
Text MD5: | 0a61293494dec29dee23d130b6d6b4c8 |
Transpilation MD5: | e4b404ad5e5c1dd7f30581e04988d005 |
Author: | stefan |
Category: | javax / gui / concepts |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2023-01-17 18:40:16 |
Source code size: | 7632 bytes / 298 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 467 / 1075 |
Version history: | 75 change(s) |
Referenced in: | [show references] |