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

357
LINES

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

JavaX fragment (include)

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

Author comment

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]