Tracking QUERY_ONLY Mode
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 can check for QUERY_ONLY without worrying about Forms'internal data structures, such as the break-up of FORMMODULE, and so on.
- You avoid the use of too many global variables and their names or, if using parameters, the process of creating them in each called form. The technique also takes care of setting them in the called form and referencing them in the calling form.
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 procedures XCALL_FORM and XNEW_FORM return the query mode of the input form. The choice to use XCALL_FORM or XNEW_FORM depends on whether the functionality desired is similar to CALL_FORM or NEW_FORM. If a CALL_FORM functionality is desired, a call to XCALL_FORM can be made ”similarly for NEW_FORM, which should be done in the calling form.
- A call to the function XQUERY_ONLY can be made in the called form to check whether it was called with the QUERY_ONLY parameter on.
- A third function, XQUERY_ONLY, returns Boolean values TRUE or FALSE, depending on whether the calling form is being called in QUERY_ONLY mode.
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;
Категории