# 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, old_cust custrec DEFINE cust_curr INT -- current position in the array (scroll cursor) DEFINE cust_touched INT -- set if something in the customer record was changed DEFINE cust_new INT -- > 0 if new customer is currently edited CONSTANT mytitle = "Customer" CONSTANT S_MOVE = "move" CONSTANT S_NEW = "new" CONSTANT S_CLOSE = "close" CONSTANT S_QUERY = "query" CONSTANT S_SAVE = "save" MAIN DEFINE state STRING -- requested state in the dialog DEFINE req_pos INT -- required position in the dialog DEFINE del INT -- delete flag OPTIONS INPUT WRAP OPEN FORM f_cust FROM "CustOrders2" DISPLAY FORM f_cust CALL cust_loadlist(9) LET state=S_MOVE LET cust_curr=1 WHILE state<>S_CLOSE DISPLAY "WHILE,state=",state,",cust_curr():",cust_curr,",req_pos:",req_pos IF cust_len() == 0 OR state == S_NEW THEN LET cust_curr=cust_len()+1 LET cust_new=cust_curr ELSE LET cust_new=0 CALL cust_fetch(req_pos) END IF LET state=S_MOVE LET req_pos=cust_curr LET cust_touched=0 DIALOG ATTRIBUTES(FIELD ORDER FORM, UNBUFFERED) INPUT cust.* FROM custrec.* ATTRIBUTES(WITHOUT DEFAULTS=NOT cust_new) AFTER FIELD cust_name IF cust.cust_name MATCHES "*[@#$%^&]*" THEN ERROR "The customer name contains invalid characters." NEXT FIELD cust_name END IF AFTER FIELD cust_address IF NOT cust_check_address(cust.cust_address) THEN NEXT FIELD cust_address END IF AFTER INPUT IF cust.cust_city IS NULL AND cust.cust_zipcode IS NOT NULL THEN CALL mbox_ok(mytitle,"You must specify a city because zipcode is not null.","stop") NEXT FIELD cust_city END IF END INPUT DISPLAY ARRAY orders TO ordlist.* BEFORE DISPLAY IF cust_new THEN CALL mbox_ok(mytitle,"Please save the new customer before adding orders!","stop") NEXT FIELD cust_name END IF BEFORE ROW MESSAGE SFMT("Order record %1 / %2", DIALOG.getCurrentRow("ordlist"), DIALOG.getArrayLength("ordlist")) ON ACTION ord_append CALL ord_append(DIALOG) ON ACTION ord_modify CALL ord_modify(DIALOG) ON ACTION ord_delete CALL ord_delete(DIALOG) END DISPLAY BEFORE DIALOG CALL cust_setup(DIALOG) CALL order_of_cust(cust_curr) IF cust_new THEN LET old_cust.* = cust.* END IF ON ACTION zoom_city CALL cust_zoom_city(DIALOG) ON ACTION cust_first LET req_pos=1 LET state=S_MOVE ; ACCEPT DIALOG ON ACTION cust_previous LET req_pos=cust_curr-1 LET state=S_MOVE ; ACCEPT DIALOG ON ACTION cust_next LET req_pos=cust_curr+1 LET state=S_MOVE ; ACCEPT DIALOG ON ACTION cust_last LET req_pos=cust_len() LET state=S_MOVE ; ACCEPT DIALOG ON ACTION cust_append LET state=S_NEW ; ACCEPT DIALOG ON ACTION cust_query --we can perform a safe query also only after LET state=S_QUERY ; ACCEPT DIALOG --the validations have been done ON ACTION cust_delete IF cust_len() > 0 THEN LET del=0 IF cust_new AND NOT cust_touched THEN LET del=1 ELSE IF mbox_yn(mytitle,"Delete current customer?","question") THEN LET del=1 END IF END IF IF del THEN CALL customers.deleteElement(cust_curr) -- normally a -- IF cust_id>0 THEN -- DELETE from customers WHERE cust_id=cust.cust_id -- END IF LET state=S_MOVE EXIT DIALOG END IF END IF ON ACTION dialogtouched LET cust_touched=1 CALL cust_setup(DIALOG) ON ACTION cust_undo LET cust.* = old_cust.* DISPLAY BY NAME cust.* CALL DIALOG.setFieldTouched("custrec.*",FALSE) LET cust_touched=0 CALL cust_setup(DIALOG) ON ACTION cust_save LET state=S_SAVE ; ACCEPT DIALOG ON ACTION cancel LET state=S_CLOSE --close is a bit difficult: if we did not touch something, --we can go out safely, otherwise we need to duplicate things --we do normally in AFTER DIALOG IF cust_touched THEN --we touched something CASE mbox_ync(mytitle,"Save your changes?","question") WHEN 1 --yes ACCEPT DIALOG --go thru the validation and fall finally out --in AFTER DIALOG WHEN 2 --no EXIT DIALOG WHEN 3 --cancel CONTINUE DIALOG END CASE ELSE --nothing changed, just go out EXIT DIALOG END IF AFTER DIALOG IF state=S_CLOSE OR state=S_SAVE THEN --we already confirmed the save, jump to the save code GOTO savedb ELSE IF old_cust.* <> cust.* THEN CASE mbox_ync(mytitle,"Save your changes?","question") WHEN 1 --yes GOTO savedb WHEN 3 --cancel and stay in the dialog LET state=S_MOVE ; CONTINUE DIALOG END CASE END IF END IF EXIT DIALOG LABEL savedb: --use the label here to save a local var and some IF's IF cust_new THEN DISPLAY "INSERT new customer into db" LET customers[cust_new].* = cust.* ELSE DISPLAY "UPDATE customer in db" LET customers[cust_curr].* = cust.* END IF END DIALOG --we can perform the query action only if everything was validated before IF state == S_QUERY THEN CALL cust_query() END IF END WHILE END MAIN FUNCTION cust_len() --we use the 'customers' array as a db replacement here --normally you could return the real COUNT of the DB statement cursor RETURN customers.getLength() END FUNCTION FUNCTION cust_fetch(index) DEFINE index INTEGER IF index > cust_len() THEN LET index = cust_len() END IF IF index < 1 THEN LET index = 1 END IF --fetch customer from the DB, index could be used to FETCH ABSOLUTE LET cust.* = customers[index].* LET old_cust.* = cust.* LET cust_curr = index MESSAGE SFMT("Fetched Customer: %1/%2", cust_curr, cust_len()) 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 cust_len() > 0 ) LET on_first = (cust_curr == 1) LET on_last = (cust_curr == cust_len()) LET cust_ro = (has_rows AND NOT cust_touched) 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) IF cust_new THEN DISPLAY "New customer (save with ctrl-s)" TO cust_info ELSE IF cust_touched THEN DISPLAY "Modifying customer (save with ctrl-s)" TO cust_info ELSE DISPLAY SFMT("Customer record %1 / %2",cust_curr,cust_len()) TO cust_info END IF END IF END FUNCTION FUNCTION cust_query() DEFINE sql STRING DISPLAY "Enter customer search criterias" TO cust_info LET int_flag = FALSE CONSTRUCT BY NAME sql ON cust_id, cust_name, cust_state, cust_city, cust_zipcode, cust_address, cust_checked IF NOT int_flag THEN -- Dummy query, should use database query instead. CALL cust_loadlist(LENGTH(sql)) CALL mbox_ok(mytitle,SFMT("Found %1 customer records in database",cust_len()),"information") END IF END FUNCTION 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) LET cust_touched = TRUE CALL cust_setup(d) END IF 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_address(value) DEFINE value STRING IF NOT cust_verify_address_format(value) THEN CALL mbox_ok(mytitle,"Address must hold a number followed by a comma and the street name.","stop") RETURN FALSE END IF RETURN TRUE END FUNCTION FUNCTION order_of_cust(index) DEFINE index INTEGER --index should be normally the cust_id --MESSAGE SFMT("Customer: %1/%2", cust_curr, cust_len()) IF index >= 1 AND index <= cust_len() THEN CALL ord_loadlist(customers[index].cust_id, (index mod 5) * 3) ELSE CALL orders.clear() END IF END FUNCTION -- Dummy function to fill the list of customers (normally, SELECT from database) FUNCTION cust_loadlist(m) 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 END FUNCTION FUNCTION ord_loadlist(mid,m) 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 CALL ord_input('A', cust.cust_id, cust.cust_name, 0) END FUNCTION FUNCTION ord_modify(d) DEFINE d ui.Dialog 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 mbox_yn(mytitle,"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