# Property of Four Js* # (c) Copyright Four Js 1995, 2019. All Rights Reserved. # * Trademark of Four Js Development Tools Europe Ltd # in the United States and elsewhere # # Four Js and its suppliers do not warrant or guarantee that these # samples are accurate and suitable for your purposes. Their inclusion is # purely for information purposes only. IMPORT FGL CityList IMPORT FGL OrderInput IMPORT FGL MsgBox GLOBALS "Globals.4gl" DEFINE cust_curr INTEGER DEFINE cust, old_cust custrec DEFINE cust_state SMALLINT CONSTANT STATE_NAVIGATE = 0 CONSTANT STATE_MODIFY = 1 CONSTANT STATE_APPEND = 2 CONSTANT otitle = "Customer" MAIN DEFINE ordlist_lastrow INTEGER OPTIONS INPUT WRAP OPEN FORM f_cust FROM "CustOrders" DISPLAY FORM f_cust DIALOG ATTRIBUTES(FIELD ORDER FORM, UNBUFFERED) INPUT cust.* FROM custrec.* ATTRIBUTES(WITHOUT DEFAULTS) ON CHANGE custrec.cust_name IF NOT cust_check_name(DIALOG) THEN NEXT FIELD CURRENT END IF ON CHANGE custrec.cust_address IF NOT cust_check_address(DIALOG) THEN NEXT FIELD CURRENT END IF ON CHANGE custrec.cust_state INITIALIZE cust.cust_city, cust.cust_zipcode, cust.cust_address TO NULL END INPUT DISPLAY ARRAY orders TO ordlist.* BEFORE ROW MESSAGE SFMT("Order record %1 / %2", DIALOG.getCurrentRow("ordlist"), DIALOG.getArrayLength("ordlist")) AFTER ROW LET ordlist_lastrow = arr_curr() END DISPLAY BEFORE DIALOG CALL cust_loadlist(DIALOG,100) CALL cust_move_to(DIALOG, 1) ON ACTION zoom_city CALL cust_zoom_city(DIALOG) ON ACTION cust_query CALL cust_query(DIALOG) ON ACTION cust_first CALL cust_move_to(DIALOG, 1) ON ACTION cust_previous CALL cust_move_to(DIALOG, cust_curr - 1) ON ACTION cust_next CALL cust_move_to(DIALOG, cust_curr + 1) ON ACTION cust_last CALL cust_move_to(DIALOG, customers.getLength()) ON ACTION dialogtouched CALL cust_touched(DIALOG) ON ACTION cust_append IF NOT cust_append(DIALOG) THEN CONTINUE DIALOG END IF NEXT FIELD cust_name ON ACTION cust_delete -- VALIDATE=NO CALL cust_delete(DIALOG) DISPLAY BY NAME cust.* ON ACTION cust_undo -- VALIDATE=NO CALL cust_undo(DIALOG) DISPLAY BY NAME cust.* ON ACTION cust_save IF NOT cust_save(DIALOG) THEN CONTINUE DIALOG END IF ON ACTION ord_append CALL ord_append(DIALOG) ON ACTION ord_modify CALL ord_modify(DIALOG) ON ACTION ord_delete CALL ord_delete(DIALOG) ON ACTION close IF cust_check_changed(DIALOG) THEN EXIT DIALOG END IF END DIALOG END MAIN FUNCTION cust_zoom_city(d) DEFINE d ui.Dialog DEFINE s INTEGER DEFINE name, state STRING CALL select_city(d.getFieldBuffer("cust_city")) RETURNING s, name, state IF s==0 THEN LET cust.cust_city = name LET cust.cust_state = state CALL d.setFieldTouched("cust_city",TRUE) CALL d.setFieldTouched("cust_state",TRUE) CALL cust_touched(d) END IF END FUNCTION FUNCTION cust_touched(d) DEFINE d ui.Dialog LET cust_state = STATE_MODIFY CALL cust_setup(d) END FUNCTION FUNCTION cust_check_changed(d) DEFINE d ui.Dialog DEFINE r INTEGER IF cust_modifying() THEN CALL cust_setup(d) LET r = mbox_ync(otitle,"Do you want to save your changes?","question") IF r == 1 THEN IF NOT cust_save(d) THEN LET r = 3 END IF END IF IF r == 2 THEN CALL cust_clean(d) END IF RETURN (r == 1 OR r == 2) END IF RETURN TRUE END FUNCTION FUNCTION cust_modifying() RETURN cust_state != STATE_NAVIGATE END FUNCTION FUNCTION cust_undo(d) DEFINE d ui.Dialog CASE cust_state WHEN STATE_MODIFY IF NOT mbox_yn(otitle,"Undo customer modification?","question") THEN RETURN END IF LET cust.* = old_cust.* CALL cust_clean(d) WHEN STATE_APPEND IF NOT mbox_yn(otitle,"Cancel customer creation?","question") THEN RETURN END IF CALL customers.deleteElement(customers.getLength()) CALL cust_clean(d) CALL cust_move_to(d, customers.getLength()) END CASE END FUNCTION FUNCTION cust_verify_address_format(value) DEFINE value STRING DEFINE num STRING, p,x INTEGER IF value IS NULL THEN RETURN TRUE END IF LET p = value.getIndexOf(",", 1) IF p == 0 THEN RETURN FALSE END IF LET num = value.subString(1, p) LET x = num IF x <= 0 THEN RETURN FALSE END IF IF value.getLength() <= p THEN RETURN FALSE END IF RETURN TRUE END FUNCTION FUNCTION cust_check_name(d) DEFINE d ui.Dialog IF LENGTH(cust.cust_name) = 0 THEN CALL mbox_ok(otitle,"You must specify a customer name.","stop") RETURN FALSE END IF IF cust.cust_name MATCHES "*[@#$%^&]*" THEN CALL mbox_ok(otitle,"The customer name containts invalid characters.","stop") RETURN FALSE END IF RETURN TRUE END FUNCTION FUNCTION cust_check_address(d) DEFINE d ui.Dialog IF NOT cust_verify_address_format(cust.cust_address) THEN CALL mbox_ok(otitle,"Address must hold a number followed by a comma and the street name.","stop") RETURN FALSE END IF RETURN TRUE END FUNCTION FUNCTION cust_check_city(d) DEFINE d ui.Dialog -- Inter-dependent field validation IF cust.cust_city IS NULL AND cust.cust_zipcode IS NOT NULL THEN CALL mbox_ok(otitle,"You must specify a city because zipcode is not null.","stop") RETURN FALSE END IF RETURN TRUE END FUNCTION FUNCTION cust_check_data(d) DEFINE d ui.Dialog IF d.validate("custrec.*") < 0 THEN RETURN "*" END IF IF NOT cust_check_name(d) THEN RETURN "custrec.cust_name" END IF IF NOT cust_check_city(d) THEN RETURN "custrec.cust_city" END IF IF NOT cust_check_address(d) THEN RETURN "custrec.cust_address" END IF RETURN NULL -- data is valid END FUNCTION FUNCTION cust_check_fields(d) DEFINE d ui.Dialog DEFINE fn STRING LET fn = cust_check_data(d) IF fn IS NULL THEN RETURN TRUE END IF IF fn != "*" THEN CALL d.nextField(fn) END IF RETURN FALSE END FUNCTION FUNCTION cust_save(d) DEFINE d ui.Dialog IF NOT cust_check_fields(d) THEN RETURN FALSE END IF IF cust_state == STATE_APPEND THEN -- SQL INSERT ... END IF IF cust_state == STATE_MODIFY THEN -- SQL UPDATE ... END IF LET customers[cust_curr].* = cust.* CALL cust_clean(d) CALL mbox_ok("Customers", "Changes saved to database", "information") CALL d.setFieldTouched("custrec.*",FALSE) CALL cust_setup(d) RETURN TRUE END FUNCTION FUNCTION cust_append(d) DEFINE d ui.Dialog IF NOT cust_autosave(d) THEN RETURN FALSE END IF INITIALIZE cust.* TO NULL CALL ord_loadlist(d,0,0) LET cust.cust_id = cust_curr + 101044 LET cust_state = STATE_APPEND CALL customers.appendElement() LET cust_curr = customers.getLength() CALL cust_setup(d) RETURN TRUE END FUNCTION FUNCTION cust_delete(d) DEFINE d ui.Dialog IF cust_curr<= 0 THEN RETURN END IF IF cust_modifying() THEN CALL cust_setup(d) IF NOT mbox_yn(otitle,"Cancel current changes?","question") THEN RETURN END IF CALL cust_clean(d) ELSE IF NOT mbox_yn(otitle,"Delete current customer?","question") THEN RETURN END IF END IF -- SQL DELETE ... CALL customers.deleteElement(cust_curr) IF customers.getLength()==0 THEN CALL mbox_ok(otitle,"No more customers in list, switching to query mode","stop") CALL cust_query(d) RETURN END IF IF cust_curr>customers.getLength() THEN LET cust_curr=customers.getLength() END IF CALL cust_move_to(d,cust_curr) CALL cust_setup(d) END FUNCTION FUNCTION cust_query(d) DEFINE d ui.Dialog DEFINE sql STRING IF NOT cust_check_changed(d) THEN RETURN END IF DISPLAY "Enter customer search criterias" TO cust_info WHILE TRUE LET int_flag = FALSE CONSTRUCT BY NAME sql ON cust_id, cust_name, cust_state, cust_city, cust_zipcode, cust_address, cust_checked ATTRIBUTES( CANCEL=FALSE, ACCEPT=FALSE ) ON ACTION close IF mbox_yn(otitle,"Quit application?","question") THEN EXIT PROGRAM END IF ON ACTION cust_fetch ACCEPT CONSTRUCT END CONSTRUCT IF NOT int_flag THEN -- Dummy query, should use database query instead. CALL cust_loadlist(d,LENGTH(sql)) CALL cust_setup(d) CALL mbox_ok(otitle,SFMT("Found %1 customer records in database",customers.getLength()),"information") EXIT WHILE END IF END WHILE END FUNCTION FUNCTION cust_setup(d) DEFINE d ui.Dialog DEFINE has_rows, on_first, on_last, cust_ro INTEGER LET has_rows = (cust_curr > 0 AND customers.getLength()>0) LET on_first = (cust_curr == 1) LET on_last = (cust_curr == customers.getLength()) LET cust_ro = (has_rows AND cust_state == STATE_NAVIGATE) CALL d.setActionActive("dialogtouched", cust_ro) CALL d.setActionActive("cust_undo", NOT cust_ro) CALL d.setActionActive("cust_save", NOT cust_ro) CALL d.setActionActive("cust_append", TRUE) -- always active CALL d.setActionActive("cust_delete", has_rows) -- can delete creation CALL d.setActionActive("cust_query", TRUE) -- always active CALL d.setActionActive("cust_first", NOT on_first) CALL d.setActionActive("cust_previous", NOT on_first) CALL d.setActionActive("cust_next", NOT on_last) CALL d.setActionActive("cust_last", NOT on_last) CALL d.setActionActive("ord_append", cust_ro) CALL d.setActionActive("ord_modify", cust_ro) CALL d.setActionActive("ord_delete", cust_ro) CASE cust_state WHEN STATE_APPEND DISPLAY "Creating a new customer record (save with ctrl-s)" TO cust_info WHEN STATE_MODIFY DISPLAY "Modifying current customer record (save with ctrl-s)" TO cust_info OTHERWISE DISPLAY SFMT("Customer record %1 / %2",cust_curr,customers.getLength()) TO cust_info END CASE END FUNCTION FUNCTION cust_clean(d) DEFINE d ui.Dialog LET old_cust.* = cust.* LET cust_state = STATE_NAVIGATE CALL cust_setup(d) END FUNCTION FUNCTION cust_autosave(d) DEFINE d ui.Dialog IF cust_modifying() THEN IF NOT cust_save(d) THEN RETURN FALSE END IF END IF RETURN TRUE END FUNCTION FUNCTION cust_move_to(d,index) DEFINE d ui.Dialog DEFINE index INTEGER IF NOT cust_autosave(d) THEN RETURN END IF IF index > customers.getLength() THEN LET index = customers.getLength() END IF IF index < 1 THEN LET index = 1 END IF LET cust.* = customers[index].* LET old_cust.* = cust.* LET cust_curr = index MESSAGE SFMT("Customer: %1/%2", cust_curr, customers.getLength()) CALL ord_loadlist(d, customers[index].cust_id, (index mod 5) * 3) CALL d.setCurrentRow("ordlist",1) CALL cust_setup(d) END FUNCTION -- Dummy function to fill the list of customers (normally, SELECT from database) FUNCTION cust_loadlist(d,m) DEFINE d ui.Dialog DEFINE m,i INTEGER CALL customers.clear() FOR i=1 TO m LET customers[i].cust_id = i LET customers[i].cust_name = "Name " || i LET customers[i].cust_state = "CA" LET customers[i].cust_city = "San Fransisco" LET customers[i].cust_zipcode = 94102 LET customers[i].cust_address = "52, Marlon Street" LET customers[i].cust_checked = i mod 2 END FOR IF customers.getLength()>0 THEN LET cust_curr = 1 LET cust.* = customers[cust_curr].* CALL ord_loadlist(d, customers[cust_curr].cust_id, (cust_curr mod 5) * 3) ELSE LET cust_curr = 0 INITIALIZE cust.* TO NULL CALL ord_loadlist(d, 0, 0) END IF CALL cust_clean(d) END FUNCTION FUNCTION ord_loadlist(d,mid,m) DEFINE d ui.Dialog DEFINE mid,m,i INTEGER CALL orders.clear() FOR i=1 TO m CALL orders.appendElement() LET orders[i].ord_id = mid * 100 + i LET orders[i].ord_crea = CURRENT YEAR TO SECOND LET orders[i].ord_amount = ( i mod 5 ) * 133.56 END FOR END FUNCTION FUNCTION ord_append(d) DEFINE d ui.Dialog IF NOT cust_check_changed(d) THEN RETURN END IF CALL ord_input('A', cust.cust_id, cust.cust_name, 0) END FUNCTION FUNCTION ord_modify(d) DEFINE d ui.Dialog IF NOT cust_check_changed(d) THEN RETURN END IF IF d.getCurrentRow("ordlist")==0 THEN RETURN END IF CALL ord_input('M', cust.cust_id, cust.cust_name, orders[d.getCurrentRow("ordlist")].ord_id) END FUNCTION FUNCTION ord_delete(d) DEFINE d ui.Dialog IF NOT cust_check_changed(d) THEN RETURN END IF IF NOT mbox_yn(otitle,"Delete select order from database?","question") THEN RETURN END IF CALL d.deleteRow("ordlist",d.getCurrentRow("ordlist")) END FUNCTION FUNCTION combo_fill_states(cb) DEFINE cb ui.ComboBox CALL cb.addItem("AK","Alaska") CALL cb.addItem("AL","Alabama") CALL cb.addItem("AR","Arkansas") CALL cb.addItem("AZ","Arizona") CALL cb.addItem("CA","California") CALL cb.addItem("CO","Colorado") CALL cb.addItem("CT","Connecticut") CALL cb.addItem("DC","D.C.") CALL cb.addItem("DE","Delaware") CALL cb.addItem("FL","Florida") CALL cb.addItem("GA","Georgia") CALL cb.addItem("HI","Hawaii") CALL cb.addItem("IA","Iowa") CALL cb.addItem("ID","Idaho") CALL cb.addItem("IL","Illinois") CALL cb.addItem("IN","Indiana") CALL cb.addItem("KS","Kansas") CALL cb.addItem("KY","Kentucky") CALL cb.addItem("LA","Louisiana") CALL cb.addItem("MA","Massachusetts") CALL cb.addItem("MD","Maryland") CALL cb.addItem("ME","Maine") CALL cb.addItem("MI","Michigan") CALL cb.addItem("MN","Minnesota") CALL cb.addItem("MO","Missouri") CALL cb.addItem("MS","Mississippi") CALL cb.addItem("MT","Montana") CALL cb.addItem("NC","North Carolina") CALL cb.addItem("ND","North Dakota") CALL cb.addItem("NE","Nebraska") CALL cb.addItem("NH","New Hampshire") CALL cb.addItem("NJ","New Jersey") CALL cb.addItem("NM","New Mexico") CALL cb.addItem("NV","Nevada") CALL cb.addItem("NY","New York") CALL cb.addItem("OH","Ohio") CALL cb.addItem("OK","Oklahoma") CALL cb.addItem("OR","Oregon") CALL cb.addItem("PA","Pennsylvania") CALL cb.addItem("PR","Puerto Rico") CALL cb.addItem("RI","Rhode Island") CALL cb.addItem("SC","South Carolina") CALL cb.addItem("SD","South Dakota") CALL cb.addItem("TN","Tennessee") CALL cb.addItem("TX","Texas") CALL cb.addItem("UT","Utah") CALL cb.addItem("VA","Virginia") CALL cb.addItem("VT","Vermont") CALL cb.addItem("WA","Washington") CALL cb.addItem("WI","Wisconsin") CALL cb.addItem("WV","West Virginia") CALL cb.addItem("WY","Wyoming") END FUNCTION