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

230
LINES

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

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

Libraryless. Click here for Pure Java version (25943L/171K).

sbool SimpleCRUD_searcher = true;

sclass SimpleCRUD<A extends Concept> {
  Concepts concepts;
  Class<A> cc;
  JTable table;
  JPanel buttons, panel;
  O renderer; // optional, func(A) -> Map
  O sorter; // optional, func(Cl<Concept>) -> Cl<Concept>
  S hID = "ID";
  Set<S> unshownFields; // not shown in table or form
  Set<S> excludeFieldsFromEditing;
  S modifiedField; // field to hold last-modified timestamp
  TableSearcher tableSearcher;
  Set<S> multiLineFields; // string fields that should be shown as text areas
  Set<S> dontDuplicateFields;
  bool latestFirst;
  int formFixer = 12; // stupid value to make submit button appear
  bool editOnDoubleClick = true;
  bool ensureIndexed;
  bool showValuesAsStruct; // BREAKING CHANGE - used to be true

  *(Class<A> cc) { this(db_mainConcepts(), cc); }
  *(Concepts *concepts, Class<A> *cc) {}
  
  SimpleCRUD<A> show(S frameTitle) {
    make();
    showFrame(frameTitle, panel);
    this;
  }
  
  SimpleCRUD<A> show() {
    ret show(plural(shortClassName(cc)));
  }
  
  SimpleCRUD<A> showMaximized() { show(); maximizeFrame(panel); ret this; }
  
  JPanel makePanel() { ret make(); }
  JPanel make() {
    db();
    framesBot();
    ret make_dontStartBots();
  }
  
  JPanel make_dontStartBots() {
    printVars("SimpleCRUD.make_dontStartBots", +concepts, +cc, count := countConcepts(cc));
    if (ensureIndexed)
      indexConceptClass(concepts, cc);

    // next line not in swing part to allow passing arguments
    // from outside like showConceptsTable_afterUpdate
    temp tempSetTL(showConceptsTable_concepts, concepts);
    temp tempSetTL(showConceptsTable_latestFirst, latestFirst);
    temp tempSetTL(showConceptsTable_sorter, sorter);
    temp tempSetTL(showConceptsTable_dropFields, asList(unshownFields));
    temp tempSetTL(dataToTable_useStruct, showValuesAsStruct);
    table = makeConceptsTable(cc, wrapRenderer(renderer));
    swing {
      buttons = jRightAlignedLine(
        jbutton("Add...", r { newConcept() }),
        tableDependButton(table, jbutton("Edit", r {
          editConcept(selectedConcept())
        })),
        tableDependButton(table, jbutton("Delete", r {
          final L<A> l = selectedConcepts();
          thread "Delete concepts" {
            withDBLock(concepts, r { for (A c : l) c.delete() });
          }
        })),
        tableDependButton(table, jbutton("Duplicate...", r {
          duplicateConcept(selectedConcept())
        })));
        
      if (SimpleCRUD_searcher) {
        tableSearcher = tableWithSearcher2(table, withMargin := true);
        panel = centerAndSouthWithMargin(tableSearcher.panel, withBottomMargin(buttons));
      } else
        panel = centerAndSouthWithMargin(table, withBottomMargin(buttons));
      
      O fEdit = voidfunc(int row) { editConcept(conceptForRow(row)) };
      tablePopupMenuItem(table, "Edit...", fEdit);
      if (editOnDoubleClick)
        onDoubleClick(table, fEdit);
    }
    ret panel;
  }
  
  O wrapRenderer(fO renderer) {
    ret renderer == null ? null : func(A a) {
      putAll(litorderedmap(hID, str(a.id)), (Map) callF(renderer, a))
    };
  }
  
  void newConcept {
    duplicateConcept(null);
  }
    
  void duplicateConcept(A oldConcept) {
    final A c = unlisted(cc);
    ccopyFieldsExcept(oldConcept, c, dontDuplicateFields);
    final Map<S, JComponent> map = makeComponents(c);
    Runnable r = r {
      concepts.register(c);
      saveData(c, map);
    };
    temp tempSetMCOpt(formLayouter1_fixer2 := formFixer);
    showFormTitled2("New " + shortClassName(cc), arrayPlus(mapToObjectArray(map), r));
  }

  void editConcept(final A c) {
    if (c == null) ret;
    final Map<S, JComponent> map = makeComponents(c);
    Runnable r = r { saveData(c, map) };
    temp tempSetMCOpt(formLayouter1_fixer2 := formFixer);
    showFormTitled2("Edit " + shortClassName(cc) + " #" + c.id, arrayPlus(mapToObjectArray(map), r));
  }
  
  A selected aka selectedConcept() {
    ret conceptForRow(selectedRow(table));
  }
  
  A conceptForRow(int row) {
    ret (A) concepts.getConcept(toLong(getTableCell(table, row, 0)));
  }
  
  int indexOfConcept(final A c) {
    if (c == null) ret -1;
    ret swing(func -> int {
      int n = tableRowCount(table);
      for row to n:
        if (toLong(getTableCell(table, row, 0)) == c.id)
          ret row;
      ret -1;
    });
  }
  
  L<A> selectedConcepts() {
    ret swing(() -> {
      int[] rows = table?.getSelectedRows();
      new L<A> l;
      fOr (int row : rows)
        l.add(conceptForRow(row));
      ret l;
    });
  }
  
  Map<S, JComponent> makeComponents(A c) {
    Map<S, JComponent> map = litorderedmap();
    makeComponents(c, map);
    ret map;
  }
  
  JComponent fieldComponent(A c, S field) {
    Class type = getFieldType(cc, field);
    O value = getOpt(c, field);
    //print("Field type: " + field + " => " + type);
    if (type == bool.class)
      ret jCenteredCheckBox(isTrue(value));
    else if (contains(multiLineFields, field) || containsNewLines(optCast S(value)))
      ret typeWriterTextArea((S) value);
    else if (isSubtype(type, Concept))
      ret jcomboboxFromConcepts_str(concepts, type, (Concept) value);
    ifclass SecretValue
    else if (type == SecretValue.class)
      ret jpassword(strOrEmpty(getVar(value/SecretValue)));
    endif
    else try {
      ret autoComboBox(valueToString(value), new TreeSet<S>(map valueToString(collect(list(concepts, cc), field))));
    } catch e {
      printException(e);
      ret jTextField(valueToString(value));
    }
  }
  
  void saveComponent(A c, S field, JComponent comp) {
    comp = unwrap(comp);
    Class type = fieldType(c, field);
    ifclass SecretValue
      if (type == SecretValue.class) {
        S text = getTextTrim((JPasswordField) comp);
        cset(c, field, empty(text) ? null : SecretValue(text));
      } else
    endif
    if (comp instanceof JTextComponent)
      cset(c, field, convertToField(trimIf(!comp instanceof JTextArea, getText((JTextComponent) comp)), cc, field));
    else if (comp cast JComboBox) {
      S text = getTextTrim(comp);
      if (isSubtype(type, Concept))
        cset(c, field, getConcept(concepts, parseFirstLong(text)));
      else
        cset(c, field, convertToField(text, cc, field));
    } else if (comp instanceof JCheckBox)
      cset(c, field, isChecked((JCheckBox) comp));
    ifclass ImageChooser
      else if (comp instanceof ImageChooser)
        cUpdatePNGFile(c, field, comp/ImageChooser.getImage(), false);
    endif
  }
  
  L<S> fields() {
    if (excludeFieldsFromEditing != null && modifiedField != null) excludeFieldsFromEditing.add(modifiedField);
    ret listWithoutSet([S field : conceptFieldsInOrder(cc) | fieldType(cc, field) != Concept.Ref.class],
      joinSets(excludeFieldsFromEditing, unshownFields));
  }
  
  void excludeFieldsFromEditing(S... fields) {
    excludeFieldsFromEditing = setPlus(excludeFieldsFromEditing, fields);
  }
  
  // override the following two methods to customize edit window
  
  void makeComponents(A c, Map<S, JComponent> map) {
    for (S field : fields())
      map.put(field, fieldComponent(c, field));
  }
  
  void saveData(A c, Map<S, JComponent> components) {
    for (S field : keys(components))
      saveComponent(c, field, components.get(field));
    if (modifiedField != null) cset(c, modifiedField, now());
  }
  
  JTable table() { ret table; }
  
  swappable S valueToString(O o) {
    ret showValuesAsStruct
      ? structureOrText_crud(o)
      : strOrNull(o);
  }
} // end of SimpleCRUD

Author comment

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: 993 / 2185
Version history: 98 change(s)
Referenced in: #1019417 - dm_SimpleCRUD [dev.] - create/read/update/delete for a list in a dynamic module
#1019715 - SimpleCRUD - older version
#1030726 - SimpleCRUD_v2 - create/read/update/delete for a concept class with simple fields [Swing]
#1034167 - Standard Classes + Interfaces (LIVE, continuation of #1003674)