There is no easy way to track whether a form is being called in query-only mode. Of course, the easiest way of using global variables works, but calling too many forms means too many global variables just for tracking the QUERY_ONLY mode. A more elegant way of doing this is by building a wrapper over CALL_FORM or NEW_FORM that returns the query-only mode of the called form.
This way, two problems are solved :
You will use the names XCALL_FORM and XNEW_FORM for the wrapper procedures. Capture the constants QUERY_ONLY or NO_QUERY_ONLY that are passed to CALL_FORM or NEW_FORM, and return them using OUT variables to the wrapper procedure. Then call the wrapper procedure rather than using CALL_FORM or NEW_FORM. Finally, check for QUERY ONLY in the called form by a simple call to the function XQUERY_ONLY, as in
IF Xquery_only() THEN END IF;
The value of the parameter formmodule_name actually refers to the form filename of the form stored in the file system. If the form is stored in the database, it refers to the form module name . This is similar to that specified in CALL_FORM and NEW_FORM. That is why the function XQUERY_ONLY also takes as input the actual filename and not the module name. This means that you cannot check query-only using :SYSTEM.CURRENT_FORM . As outlined in the Myths About the Form Filename, Form Module Name, and Form ID section, you can always get the form filename from the form module name and then use XQUERY_ONLY to check it. If the form module name is to be passed, the parameter formmodule_name has to be of type FORMMODULE.
You achieve the desired functionality in the following steps:
The code is as follows :
PACKAGE Xcallform IS PROCEDURE XCALL_FORM(formmodule_name VARCHAR2, display NUMBER DEFAULT HIDE, switch_menu NUMBER DEFAULT NO_REPLACE, query_mode IN OUT NUMBER DEFAULT NO_QUERY_ONLY, data_mode NUMBER DEFAULT NO_SHARE_LIBRARY_DATA, paramlist_id PARAMLIST DEFAULT NULL); PROCEDURE XNEW_FORM(formmodule_name VARCHAR2, rollback_mode NUMBER DEFAULT TO_SAVEPOINT, query_mode IN OUT NUMBER DEFAULT NO_QUERY_ONLY, data_mode NUMBER DEFAULT NO_SHARE_LIBRARY_DATA, paramlist_id PARAMLIST DEFAULT NULL); FUNCTION XQUERY_ONLY(FormModule_name VARCHAR2) RETURN BOOLEAN; END Xcallform; PACKAGE BODY Xcallform IS TYPE xcallform_rec is RECORD (formmodule_name VARCHAR2(40), queryonly NUMBER); TYPE Xcallform_tab IS TABLE OF xcallform_rec INDEX BY BINARY_INTEGER; Xcallform_stack xcallform_tab; FUNCTION getstackcount RETURN NUMBER IS BEGIN RETURN(xcallform_stack.count); END; Procedure XCALL_FORM(formmodule_name VARCHAR2, display NUMBER DEFAULT HIDE, switch_menu NUMBER DEFAULT NO_REPLACE, query_mode IN OUT NUMBER DEFAULT NO_QUERY_ONLY, data_mode NUMBER DEFAULT NO_SHARE_LIBRARY_DATA, paramlist_id PARAMLIST DEFAULT NULL) IS cnt BINARY_INTEGER := getstackcount; current_form_before FORMMODULE; current_form_after FORMMODULE; BEGIN /* Populate the next element of this array */ xcallform_stack(cnt+1).formmodule_name := formmodule_name; xcallform_stack(cnt+1).queryonly := query_mode; current_form_before := FIND_FORM(NAME_IN('SYSTEM.CURRENT_FORM')); CALL_FORM(formmodule_name, display, switch_menu, query_mode, data_mode, paramlist_id); current_form_after := FIND_FORM(NAME_IN('SYSTEM.CURRENT_FORM')); /* If call_form is not a success, delete added stack element */ IF (current_form_before.id = current_form_after.id) THEN xcallform_stack.delete(cnt+1); END IF; END; PROCEDURE XNEW_FORM(formmodule_name VARCHAR2, rollback_mode NUMBER DEFAULT TO_SAVEPOINT, query_mode IN OUT NUMBER DEFAULT NO_QUERY_ONLY, data_mode NUMBER DEFAULT NO_SHARE_LIBRARY_DATA, paramlist_id PARAMLIST DEFAULT NULL) IS cnt BINARY_INTEGER := getstackcount; current_form_before FORMMODULE; current_form_after FORMMODULE; BEGIN /* Populate the next element of this array */ xcallform_stack(cnt+1).formmodule_name := formmodule_name; xcallform_stack(cnt+1).queryonly := query_mode; NEW_FORM(formmodule_name, rollback_mode, query_mode, data_mode, paramlist_id); current_form_after := FIND_FORM(NAME_IN('SYSTEM.CURRENT_FORM')); /* If new_form is not a success, delete added stack element */ IF (current_form_before.id = current_form_after.id) THEN xcallform_stack.delete(cnt+1); END IF; END; FUNCTION XQUERY_ONLY(formmodule_name VARCHAR2) RETURN BOOLEAN IS queryonly NUMBER; BEGIN FOR I IN 1..getstackcount LOOP IF xcallform_stack(i).formmodule_name = formmodule_name THEN queryonly := xcallform_stack(i).queryonly; END IF; END LOOP; IF queryonly = 501 THEN RETURN TRUE; ELSIF queryonly = 502 THEN RETURN FALSE; END IF; END; END Xcallform;
You can also use XCALL_FORM or XNEW_FORM to call forms using CALL_FORM or NEW_FORM, respectively. This is in the calling form, for example, in the appropriate WHEN-BUTTON-PRESSED trigger in the calling form:
XCALL_FORM(, NO_HIDE, NO_REPLACE, QUERY_ONLY);
You call the function XQUERY_ONLY passing the called form module name in the called form. The return values of this function will determine whether the form is in query-only mode. For example, in the WHEN-NEW-FORM-INSTANCE of the called form, a warning message can be displayed to notify users of the query-only mode, even before they start working on it:
WHEN-NEW-FORM-INSTANCE DECLARE v_msg VARCHAR2(1000); BEGIN IF XQUERY_ONLY(:SYSTEM.CURRENT_FORM) THEN v_msg := 'This form is running in Query Only mode, '; v_msg := v_msg'cannot make database changes!'; p_show_alert(v_msg); END IF;
This package should be included in a Forms PL/SQL library or an object library and attached to both the calling and called forms. The calls to CALL_FORM and NEW_FORM should be made with the data_mode parameter set to SHARE_LIBRARY_DATA. If the call to CALL_FORM or NEW_FORM is being made with the data mode parameter set to NO_SHARE_LIBRARY_DATA (which is the default), and this package is included in a library attached to both the calling form and called form, then instead of the PL/SQL table of records, use a GLOBAL_SCOPE record group . The package XCALLFORM with such an implementation is given here:
PACKAGE Xcallform IS PROCEDURE XCALL_FORM(formmodule_name VARCHAR2, display NUMBER DEFAULT HIDE, switch_menu NUMBER DEFAULT NO_REPLACE, query_mode IN OUT NUMBER DEFAULT NO_QUERY_ONLY, data_mode NUMBER DEFAULT NO_SHARE_LIBRARY_DATA, paramlist_id PARAMLIST DEFAULT NULL); PROCEDURE XNEW_FORM(formmodule_name VARCHAR2, rollback_mode NUMBER DEFAULT TO_SAVEPOINT, query_mode IN OUT NUMBER DEFAULT NO_QUERY_ONLY, data_mode NUMBER DEFAULT NO_SHARE_LIBRARY_DATA, paramlist_id PARAMLIST DEFAULT NULL); FUNCTION XQUERY_ONLY(formmodule_name VARCHAR2) RETURN BOOLEAN; END Xcallform; PACKAGE BODY xcallform IS rg_id RECORDGROUP; gc_id1 GROUPCOLUMN; gc_id2 GROUPCOLUMN; PROCEDURE p_global_record_group IS BEGIN rg_id := FIND_GROUP('RG_GLOBAL'); IF NOT ID_NULL(rg_id) THEN DELETE_GROUP(rg_id); END IF; rg_id := CREATE_GROUP('RG_GLOBAL', GLOBAL_SCOPE); IF ID_NULL(rg_id) THEN MESSAGE('ERR: XQUERY_ONLY failure.!'); RAISE FORM_TRIGGER_FAILURE; END IF; gc_id1 := ADD_GROUP_COLUMN(rg_id, 'formmodule_name', CHAR_COLUMN, 40); gc_id2 := ADD_GROUP_COLUMN(rg_id, 'queryonly', NUMBER_COLUMN); END; FUNCTION getrgcount(i_rg_id RECORDGROUP) RETURN NUMBER IS BEGIN p_global_record_group; RETURN(GET_GROUP_ROW_COUNT(i_rg_id)); END; PROCEDURE XCALL_FORM(formmodule_name VARCHAR2, display NUMBER DEFAULT HIDE, switch_menu NUMBER DEFAULT NO_REPLACE, query_mode IN OUT NUMBER DEFAULT NO_QUERY_ONLY, data_mode NUMBER DEFAULT NO_SHARE_LIBRARY_DATA, paramlist_id PARAMLIST DEFAULT NULL) IS cnt NUMBER := NVL(getrgcount(rg_id),0); current_form_before FORMMODULE; current_form_after FORMMODULE; BEGIN /* Populate the next element of this array */ ADD_GROUP_ROW(rg_id, cnt+1); SET_GROUP_CHAR_CELL(gc_id1, cnt+1, formmodule_name); SET_GROUP_NUMBER_CELL(gc_id2, cnt+1, query_mode); CALL_FORM(formmodule_name, display, switch_menu, query_mode, data_mode, paramlist_id); /* If call_form is not a success, delete added stack element */ IF (current_form_before.id = current_form_after.id) THEN DELETE_GROUP_ROW(rg_id, cnt+1); END IF; END; PROCEDURE XNEW_FORM(formmodule_name VARCHAR2, Rollback_mode NUMBER DEFAULT TO_SAVEPOINT, query_mode IN OUT NUMBER DEFAULT NO_QUERY_ONLY, data_mode NUMBER DEFAULT NO_SHARE_LIBRARY_DATA, paramlist_id PARAMLIST DEFAULT NULL) IS cnt NUMBER := NVL(getrgcount(rg_id),0); current_form_before FORMMODULE; Current_form_after FORMMODULE; BEGIN /* Populate the next element of this array */ ADD_GROUP_ROW(rg_id, cnt+1); SET_GROUP_CHAR_CELL(gc_id1, cnt+1, formmodule_name); SET_GROUP_NUMBER_CELL(gc_id2, cnt+1, query_mode); NEW_FORM(formmodule_name, rollback_mode, query_mode, data_mode, paramlist_id); /* If new_form is not a success, delete added stack element */ IF (current_form_before.id = current_form_after.id) THEN DELETE_GROUP_ROW(rg_id, cnt+1); END IF; END; FUNCTION XQUERY_ONLY(FormModule_name VARCHAR2) RETURN BOOLEAN IS queryonly NUMBER; BEGIN FOR I IN 1 .. getrgcount(rg_id) LOOP IF GET_GROUP_CHAR_CELL(gc_id1, I) = formmodule_name THEN queryonly := GET_GROUP_NUMBER_CELL(gc_id2, I); END IF; END LOOP; IF queryonly = 501 THEN RETURN TRUE; ELSIF queryonly = 502 THEN RETURN FALSE; END IF; END; END Xcallform;
GUI Development
Advanced GUI Development: Developing Beyond GUI
Multi-form Applications
Advanced Forms Programming
Error-Message Handling
Object-oriented Methods in Forms
Intelligence in Forms
Additional Interesting Techniques
Working with Trees
Oracle 8 and 8i Features in Forms Developer