Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

470
LINES

< > BotCompany Repo | #1030726 // SimpleCRUD_v2 - create/read/update/delete for a concept class with simple fields [Swing]

JavaX fragment (include) [tags: use-pretranspiled]

Transpiled version (35238L) is out of date.

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  
  settable bool showDeleteButton = true;
21  
  settable bool showDuplicateButton = true;
22  
  settable bool iconButtons;
23  
  settable bool editWindowScrollable;
24  
  settable bool scrollableButtons; // if you have a lot of them
25  
  
26  
  // put a component in here to fill the space left of the buttons
27  
  SingleComponentPanel scpButtonFiller;
28  
  
29  
  JComponent wrappedButtonBar;
30  
  
31  
  // After user has submitted edit/create window
32  
  event conceptSaved(A concept);
33  
34  
  *(Class<A> conceptClass) { super(conceptClass); }
35  
  *(Concepts concepts, Class<A> conceptClass) { super(concepts, conceptClass); }
36  
  
37  
  SimpleCRUD_v2<A> show(S frameTitle) {
38  
    make();
39  
    showFrame(frameTitle, panel);
40  
    this;
41  
  }
42  
  
43  
  SimpleCRUD_v2<A> show() {
44  
    ret show(entityNamePlural());
45  
  }
46  
  
47  
  SimpleCRUD_v2<A> showMaximized() { show(); maximizeFrame(panel); ret this; }
48  
  
49  
  JPanel makePanel() { ret make(); }
50  
  JPanel make() {
51  
    db();
52  
    framesBot();
53  
    ret make_dontStartBots();
54  
  }
55  
  
56  
  swappable MapSO itemToMap_inner(A a) {
57  
    ret super.itemToMap_base(a);
58  
  }
59  
  
60  
  MapSO itemToMap_base(A a) {
61  
    MapSO map = itemToMap_inner(a);
62  
    if (map == null) null;
63  
    ret putAll(map, moreSpecialFieldsForItem(a));
64  
  }
65  
  
66  
  // shown on the right (usually)
67  
  swappable MapSO moreSpecialFieldsForItem(A a) {
68  
    MapSO map = litorderedmap();
69  
    if (showBackRefs) {
70  
      Cl<Concept> refs = allBackRefs(a);
71  
      if (nempty(refs)) {
72  
        refs = sortedByConceptID(refs);
73  
        int more = l(refs)-maxRefsToShow;
74  
        map.put("Referenced by",
75  
          joinWithComma(takeFirst(maxRefsToShow, refs))
76  
        + (more > 0 ? ", " + more + " more" : ""));
77  
      }
78  
    }
79  
    ret map;
80  
  }
81  
  
82  
  public JPanel make_dontStartBots() { ret (JPanel) visualize(); }
83  
  
84  
  cachedVisualize {
85  
    dropFields = asList(unshownFields);
86  
    makeTable();
87  
    swing {
88  
      JButton deleteButton = !showDeleteButton ? null
89  
        : tableDependButton(table, makeDeleteButton(r {
90  
          L<A> l = selectedConcepts();
91  
          withDBLock(concepts, r { for (A c : l) c.delete() });
92  
        }));
93  
94  
      buttons = jRightAlignedLine(flattenToList(
95  
        !showAddButton ? null : makeAddButton(r newConcept),
96  
        !showEditButton || !showDuplicateButton ? null : tableDependButton(table, makeDuplicateButton(r {
97  
          duplicateConcept(selectedConcept())
98  
        })),
99  
        !showEditButton ? null : tableDependButton(table, makeEditButton(r {
100  
          editConcept(selectedConcept())
101  
        })),
102  
        deleteButton,
103  
      ));
104  
      
105  
      wrappedButtonBar = wrapButtonBar(centerAndEastWithMargin(scpButtonFiller = singleComponentPanel(), buttons));
106  
      
107  
      if (showSearchBar) {
108  
        tableSearcher = tableWithSearcher2(table, withMargin := true);
109  
        panel = centerAndSouthWithMargin(tableSearcher.panel, wrappedButtonBar);
110  
      } else
111  
        panel = centerAndSouthWithMargin(table, wrappedButtonBar);
112  
      
113  
      var fEdit = voidfunc(int row) {
114  
        editConcept(getItem(row))
115  
      };
116  
      tablePopupMenuItem(table, "Edit", fEdit);
117  
      onDoubleClick(table, fEdit);
118  
      tablePopupMenuFirst(table, (menu, row) -> {
119  
        Concept c = getItem(row);
120  
        if (c != null)
121  
          addMenuItem(menu, "Delete " + quote(shorten(str(c))), rThread {
122  
            deleteConcept(c);
123  
          });
124  
      });
125  
    } // end of swing
126  
    ret panel;
127  
  }
128  
  
129  
  swappable JComponent wrapButtonBar(JComponent buttonBar) {
130  
    if (scrollableButtons)
131  
      ret jBorderlessHigherHorizontalScrollPane(vstack(buttonBar));
132  
    ret buttonBar;
133  
  }    
134  
135  
  swappable void newConcept() {
136  
    duplicateConcept(null);
137  
  }
138  
  
139  
  swappable A createDuplicate(A c) {
140  
    A clone = (A) unlisted(_getClass(c));
141  
    ccopyFieldsExcept(c, clone, dontDuplicateFields);
142  
    ret clone;
143  
  }
144  
    
145  
  void duplicateConcept(A oldConcept) {
146  
    new EditWindow ew;
147  
    if (oldConcept == null)
148  
      ew.item = unlisted(conceptClass);
149  
    else
150  
      ew.item = createDuplicate(oldConcept);
151  
152  
    csetAll(ew.item, filters);
153  
    makeComponents(ew);
154  
    
155  
    // save action
156  
    F0<Bool> r = func -> bool {
157  
      try {
158  
        selectAfterUpdate(ew.item);
159  
        concepts.register(ew.item);
160  
        saveData(ew);
161  
      } catch print e {
162  
        infoBox(e);
163  
        false;
164  
      }
165  
      true;
166  
    };
167  
    temp tempSetMCOpt(formLayouter1_fixer2 := formFixer);
168  
    S title = "New " + entityName();
169  
    title += appendBracketed(javaClassDescForItem(ew.item));
170  
    
171  
    renameSubmitButton("Create", showAForm(title,
172  
      toObjectArray(listPlus(ew.matrix, r))));
173  
  }
174  
175  
  void editConcept(A c) {
176  
    if (c == null) ret;
177  
    new EditWindow ew;
178  
    ew.item = c;
179  
    makeComponents(ew);
180  
    F0<Bool> r = func -> bool {
181  
      try {
182  
        // concept class was changed, replace object
183  
        if (ew.item != c) {
184  
          print("Replacing object: " + c + " => " + ew.item);
185  
          replaceConceptAndUpdateRefs(c, ew.item);
186  
        }
187  
        saveData(ew);
188  
      } catch print e {
189  
        infoBox(e);
190  
        false;
191  
      }
192  
      true;
193  
    };
194  
    temp tempSetMCOpt(formLayouter1_fixer2 := formFixer);
195  
    renameSubmitButton("Save", showAForm("Edit " + entityName() + " #" + c.id,
196  
      toObjectArray(listPlus(ew.matrix, r))));
197  
  }
198  
  
199  
  JComponent fieldComponent(A c, S field) {
200  
    Class type = getFieldType(c.getClass(), field);
201  
    Type genericType = genericFieldType(conceptClass, field);
202  
    if (type == Concept.Ref.class)
203  
      type = getTypeArgumentAsClass(genericType);
204  
    
205  
    O value = cget(c, field);
206  
    if (type == null) type = _getClass(value);
207  
    //print("Field type: " + field + " => " + type);
208  
    if (type == bool.class)
209  
      ret jCenteredCheckBox(isTrue(value));
210  
    else if (contains(multiLineFields, field) || containsNewLines(optCast S(value)))
211  
      ret makeTextArea((S) value);
212  
    else if (isSubtype(type, Concept))
213  
      ret jcomboboxFromConcepts_str(concepts, type, (Concept) value);
214  
    ifclass SecretValue
215  
    else if (type == SecretValue.class)
216  
      ret jpassword(strOrEmpty(getVar(value/SecretValue)));
217  
    endif
218  
    else if (isUneditableFieldType(type))
219  
      ret jlabel(structureOrText_crud(value));
220  
    else try {
221  
      ret autoComboBox(structureOrText_crud(value), new TreeSet<S>(map structureOrText_crud(collect(list(concepts, conceptClass), field))));
222  
    } catch e {
223  
      printException(e);
224  
      ret jTextField(structureOrText_crud(value));
225  
    }
226  
  }
227  
  
228  
  void saveComponent(A c, S field, JComponent comp) {
229  
    comp = unwrap(comp);
230  
    ifdef SimpleCRUD_v2_debug
231  
      printVars_str("saveComponent", +field, +comp);
232  
    endifdef
233  
    
234  
    Class type = fieldType(c, field);
235  
    Type genericType = genericFieldType(conceptClass, field);
236  
    if (type == Concept.Ref.class)
237  
      type = getTypeArgumentAsClass(genericType);
238  
      
239  
    ifclass SecretValue
240  
      if (type == SecretValue.class) {
241  
        S text = getTextTrim((JPasswordField) comp);
242  
        cset(c, field, empty(text) ? null : SecretValue(text));
243  
      } else
244  
    endif
245  
    if (comp instanceof JTextComponent) {
246  
      S text = trimIf(!comp instanceof JTextArea, getText((JTextComponent) comp));
247  
      O value = postProcessValue(text);
248  
      O converted = convertToField(value, c.getClass(), field);
249  
      ifdef SimpleCRUD_v2_debug
250  
        printVars_str("saveComponent", +field, +text, +value, +converted);
251  
      endifdef
252  
      cset(c, field, converted);
253  
    }
254  
    else if (comp cast JComboBox) {
255  
      S text = getTextTrim(comp);
256  
      if (isSubtype(type, Concept))
257  
        cset(c, field, getConcept(concepts, parseFirstLong(text)));
258  
      else {
259  
        O value = postProcessValue(text);
260  
        cset(c, field, convertToField(value, c.getClass(), field));
261  
      }
262  
    } else if (comp instanceof JCheckBox)
263  
      cset(c, field, isChecked((JCheckBox) comp));
264  
    ifclass ImageChooser
265  
      else if (comp instanceof ImageChooser)
266  
        cUpdatePNGFile(c, field, comp/ImageChooser.getImage(), false);
267  
    endif
268  
  }
269  
  
270  
  swappable Cl<S> editableFieldsForItem(A c) {
271  
    if (excludeFieldsFromEditing != null && modifiedField != null) excludeFieldsFromEditing.add(modifiedField);
272  
    ret listWithoutSet(
273  
      conceptFieldsInOrder(c),
274  
      joinSets(excludeFieldsFromEditing, unshownFields, keys(filters)));
275  
  }
276  
  
277  
  void excludeFieldsFromEditing(S... fields) {
278  
    excludeFieldsFromEditing = setPlus(excludeFieldsFromEditing, fields);
279  
  }
280  
  
281  
  
282  
  Cl<Class<? extends A>> possibleClasses() {
283  
    ret (Cl) moveItemFirst(conceptClass, dropTypeParameter(sortClassesByNameIC(myNonAbstractClassesImplementing(conceptClass))));
284  
  }
285  
  
286  
  JComboBox<Class<? extends A>> classSelectorComponent(A c) {
287  
    ret setComboBoxRenderer(jTypedComboBox(possibleClasses(), _getClass(c)),
288  
      customToStringListCellRenderer shortClassName());
289  
  }
290  
  
291  
  class NewField {
292  
    JTextField tfName = jtextfield();
293  
    JTextField tfValue = jtextfield();
294  
    JComboBox cbRef = jcomboboxFromConcepts_str(concepts, conceptClass);
295  
    JComboBox cbType = jcombobox("String", "Reference");
296  
    SingleComponentPanel scpValue = singleComponentPanel();
297  
    
298  
    JPanel panel() {
299  
      onChangeAndNow(cbType, r updateSCP);
300  
      ret jhgridWithSpacing(
301  
        withToolTip("Name for new field", withLabel("Name", tfName)),
302  
        withLabel("Value", westAndCenterWithMargin(cbType, scpValue)));
303  
    }
304  
    
305  
    S typeStr() { ret getText(cbType); }
306  
    
307  
    void updateSCP {
308  
      scpValue.setComponent(eqic(typeStr(), "Reference")
309  
        ? cbRef
310  
        : withToolTip("Contents of new field", tfValue));
311  
    }
312  
    
313  
    S field() { ret gtt(tfName); }
314  
    O value() {
315  
      ret eqic(typeStr(), "Reference")
316  
        ? getConcept(concepts, parseFirstLong(getText(cbRef)))
317  
        : gtt(tfValue);
318  
    }
319  
  }
320  
  
321  
  class EditWindow {
322  
    A item;
323  
    Map<S, JComponent> componentsByField = litorderedmap();
324  
    new L matrix; // label, component, label, component, ...
325  
    JComboBox<Class<? extends A>> classSelector;
326  
    new L<NewField> newFields;
327  
  }
328  
  
329  
  // override the following two methods to customize edit window
330  
  
331  
  void makeComponents(EditWindow ew) {
332  
    // class selector
333  
    
334  
    if (showClassNameSelectors) {
335  
      addAll(ew.matrix, makeLabel("Java Class"), ew.classSelector = classSelectorComponent(ew.item));
336  
      onChange(ew.classSelector, r {
337  
        Class<? extends A> oldClass = _getClass(ew.item);
338  
        Class<? extends A> newClass = getSelectedItem_typed(ew.classSelector);
339  
        if (oldClass == newClass) ret;
340  
        A oldItem = ew.item;
341  
        ew.item = unlisted(newClass);
342  
        ccopyFields(oldItem, ew.item);
343  
      });
344  
    }
345  
      
346  
    // regular fields
347  
    
348  
    for (S field : editableFieldsForItem(ew.item)) {
349  
      JComponent c = fieldComponent(ew.item, field);
350  
      ew.componentsByField.put(field, c);
351  
      addAll(ew.matrix, makeLabel(field), c);
352  
    }
353  
      
354  
    // new fields
355  
    
356  
    if (allowNewFields && newFieldsToShow > 0) {
357  
      addAll(ew.matrix, " ", jlabel()); // spacing
358  
      for i to newFieldsToShow: {
359  
        new NewField nf;
360  
        ew.newFields.add(nf);
361  
        addAll(ew.matrix, makeLabel(""/*"New field"*/), nf.panel());
362  
      }
363  
    }
364  
  }
365  
  
366  
  void saveData(EditWindow ew) {
367  
    // save regular fields
368  
    
369  
    for (S field, JComponent component : ew.componentsByField)
370  
      if (isIdentifier(field))
371  
        saveComponent(ew.item, field, component);
372  
        
373  
    // save new fields
374  
    
375  
    for (NewField nf : ew.newFields) {
376  
      S field = nf.field();
377  
      O value = nf.value();
378  
      if (nempty(field) && notNullOrEmptyString(value))
379  
        cset(ew.item, field, value);
380  
    }
381  
        
382  
    if (modifiedField != null) cset(ew.item, modifiedField, now());
383  
    
384  
    conceptSaved(ew.item);
385  
  }
386  
  
387  
  O postProcessValue(O o) {
388  
    if (emptyStringsToNull && eq(o, "")) null;
389  
    ret o;
390  
  }
391  
  
392  
  // labels on left hand side of form
393  
  JComponent makeLabel(S label) {
394  
    ret jMinWidthAtLeast(formLabelsWidth, jlabel(label));
395  
  }
396  
  
397  
  swappable JComponent showAForm(S title, O... parts) {
398  
    JForm form = JForm(parts).disposeFrameOnSubmit();
399  
    var panel = form.visualize();
400  
    showFrame(title, editWindowScrollable ? jscroll_vertical(panel) : panel);
401  
    ret panel;
402  
  }
403  
  
404  
  bool isUneditableFieldType(Class type) {
405  
    ret isSubclassOfAny(type, Map, Cl, Pair);
406  
  }
407  
  
408  
  void hideField(S field) { hideFields(field); }
409  
  void hideFields(S... fields) {
410  
    unshownFields = createOrAddToSet(unshownFields, fields);
411  
  }
412  
  
413  
  selfType entityName(S name) { entityName = if0_const(name); this; }
414  
  
415  
  swappable S entityName() { ret shortClassName(conceptClass); }
416  
  swappable S entityNamePlural() { ret plural(entityName()); }
417  
  swappable S entityNameLower() { ret firstToLower(entityName()); }
418  
  swappable S entityNamePluralLower() { ret firstToLower(entityNamePlural()); }
419  
  
420  
  JPanel buttons() { visualize(); ret buttons; }
421  
  
422  
  void addButtonInFront(S text, Runnable r) {
423  
    addButtonInFront(jbutton(text, r));
424  
  }
425  
  
426  
  void addButton(S text, Runnable r) {
427  
    addButton(jbutton(text, r));
428  
  }
429  
  
430  
  void addButton(JComponent c) {
431  
    buttons().add(c);
432  
  }
433  
  
434  
  void addButtonInFront(JComponent c) {
435  
    addComponentInFront(buttons(), c);
436  
  }
437  
  
438  
  JButton makeAddButton(Runnable r) { ret makeButton(
439  
    "Add...", "Create a new " + entityNameLower(), #1103069, r); }
440  
  JButton makeDeleteButton(Runnable r) { ret makeButton(
441  
    "Delete", "Delete selected " + entityNamePluralLower(), #1103067, r); }
442  
  JButton makeEditButton(Runnable r) { ret makeButton(
443  
    "Edit...", "Edit selected " + entityNameLower(), #1103068, r); }
444  
  JButton makeDuplicateButton(Runnable r) { ret makeButton(
445  
    "Duplicate...", "Duplicate selected " + entityNameLower(), #1103070, r); }
446  
  
447  
  JButton makeButton(S text, S toolTip default null, S iconID, Runnable r) {
448  
    if (iconButtons)
449  
      ret toolTip(or2(toolTip, text), jimageButtonScaledToWidth(16, iconID, text, r));
450  
    else
451  
      ret toolTip(toolTip, jbutton(text, r));
452  
  }
453  
  
454  
  void multiLineField(S field) {
455  
    multiLineFields = addOrCreate(multiLineFields, field);
456  
  }
457  
  
458  
  swappable JComponent makeTextArea(S text) {
459  
    ret typeWriterTextArea(text);
460  
  }
461  
  
462  
  void setButtonFiller(JComponent c) {
463  
    scpButtonFiller.set(c);
464  
  }
465  
  
466  
  TableSearcher tableSearcher() {
467  
    visualize();
468  
    ret tableSearcher;
469  
  }
470  
} // end of SimpleCRUD_v2

Author comment

Began life as a copy of #1006540

download  show line numbers  debug dex  old transpilations   

Travelled to 5 computer(s): bhatertpkbcr, ekrmjmnbrukm, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt

No comments. add comment

Snippet ID: #1030726
Snippet name: SimpleCRUD_v2 - create/read/update/delete for a concept class with simple fields [Swing]
Eternal ID of this version: #1030726/134
Text MD5: 6b0e0bbefd12dcfc2bd0c6b01ade872e
Author: stefan
Category: javax / concepts / gui
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2023-10-15 13:29:11
Source code size: 15416 bytes / 470 lines
Pitched / IR pitched: No / No
Views / Downloads: 578 / 1474
Version history: 133 change(s)
Referenced in: [show references]