# ****************************************************
# Custom functions for performing stress testing of
# Citrix's WinFrame server.
# The functions can also be used for performing multiple
# window actions.
# Created by: Ferry Firmansjah (firmanf@usa.net)
# Last Updated on: November 1, 1998
# Tested with Mercury Interactive's LoadRunner 4.51 and
#    WinRunner 4.04 (and 4.03).
# Information about the use of this module can be found at
# the Tech Page @ toyland.net.
#    http://www.toyland.net/tech
#
# All trademarks are the property of their respective owners.
# This module is available at no charge.
# ****************************************************

# ****
# Global Variables that need to be defined in the test script:
# WF_USE_MACHINENAME: TRUE (1) or FALSE (0). Whether to use username
#    or machine name to uniquely identify the index to use to
#    assign WinFrame UID's (used by the WF_load_UID function).
# DELAY_USER: TRUE (1) or FALSE (0).  If TRUE, a check_user_delay ()
#    function call will cause the current user to be delayed by the
#    value of DELAY_USER_TIME.
# DELAY_USER_TIME: the number of seconds to delay (if DELAY_USER is TRUE).
# WF_MENU_MOUSE_CLICK: TRUE or FALSE.  Whether to use mouse click or
#    keystrokes to select menu items.  Used by the WF_load_menu_item and
#    multiple_win_menu_select_item function.  If you don't use these
#    functions, you do not need to set this parameter
# ****

####
# Syntax:
# Return value:
# Parameters:
#    xxx:
# Description:
#
####



####
# Syntax: multiple_win_wait_text (win_names_ra[], wait_string, max_wait, x1, y1, x2, y2);
# Return value: default return values
# Parameters:
#    win_names_ra: array of window names
#    wait_string: string to wait for
#    max_wait: the maximum amount of seconds to wait
#    x1, y1, x2, y2: the rectangular area to search for the text
# Description: Waits for a text to appear in the specified windows.
####
public function multiple_win_wait_text (win_names_ra[], wait_str, max_wait, x1, y1, x2, y2) {
	auto arg_num = nargs(), res, start_time, tmp;
	auto i, win_names_racount, res_ra[], count;
	if (arg_num != 7)
		return (report_param_msg ("multiple_win_wait_text_WR - "& arg_num));

	win_names_racount = array_length (win_names_ra);

	count = 0;
	start_time = get_time();
	do {
		for (i = 0; i < win_names_racount; i++) {
			if (res_ra[i] != E_OK) {
				win_activate (win_names_ra[i]);
				res_ra[i] = ((win_get_text (win_names_ra[i], tmp, x1, y1, x2, y2) == E_OK) ? ((tmp == wait_str) ? E_OK : E_GENERAL_ERROR) : E_GENERAL_ERROR );
				if (res_ra[i] == E_OK) {
					count++;
				}
			}
		}
	} while (((get_time () - start_time) < max_wait) &&  (count < win_names_racount));
	for (i = 0; i < win_names_racount; i++) {
		if (res_ra[i] != E_OK) error_message ("The text '"& wait_str &"' does not appear in window "& win_names_ra[i]);
	}
	return (win_names_racount - count);
}

####
# Syntax: WF_multiple_win_synchronize (win_names_ra[][, UID_ra[]]);
# Return value: E_OK
# Parameters:
#    win_names_ra: array of window names
#    UID_ra: array of user ID's that correspond to the index in the win_names_ra
# Description: Updates the win_names_ra according to which window is still open.
#    If a session window has closed, it's entry will be dropped from the
#    array, and the rest of the array's entries shift down.
#    If UID_ra is also provided, then the corresponding UID_ra will also be updated.
####
public function WF_multiple_win_synchronize (win_names_ra[], UID_ra[]) {
	auto win_names_racount, i, j, deleted_win_names_ra[];
	auto update_UID = FALSE;
	if (nargs() == 2) update_UID = TRUE;
	else {
		if (nargs() != 2) {
			return (report_param_msg ("WF_multiple_win_synchronize"));
		}
	}
	win_names_racount = array_length (win_names_ra);
	for (i = 0; i < win_names_racount; i++) {
		if (win_names_ra[i] == "" || win_exists (win_names_ra[i]) != E_OK) {
			output_message ("Synchronization: Window '"& win_names_ra[i] &"' has disappeared!");
			delete win_names_ra[i];
			if (update_UID) {
				delete UID_ra[i];
			}
			for (j = i; j < win_names_racount - 1; j++) {
				win_names_ra[j] = win_names_ra[j + 1];
				if (update_UID) {
					UID_ra[j] = UID_ra[j + 1];
				}
			}

			# delete the last entry
			delete win_names_ra [j];
			if (update_UID) {
				delete UID_ra[j];
			}
			i--;
			win_names_racount--;
		}
	}
	return win_names_racount;
}

####
# Syntax: WF_load_UID (fname, UID_ra[], UID_racount);
# Return value: default return values
# Parameters:
#    fname: the name of the file that stores the UID information
#    UID_ra: the target array to be filled with user ID's
#    UID_racount: the total number of arrays added
# Description: Loads the list of UID's for a particular Windows NT login name
#    This used for multiple WF window testing.
#    The file fname will contain the following information, tab delimited:
#      Machine_name	Machine_login_id	WinFrame_IDs_to_use
#    You can either use the Machine's name or Login ID as the unique identifier
#    for each machine, by setting a global variable WF_USE_MACHINENAME to either TRUE
#    or FALSE (default is TRUE).  The WinFrame IDs are semi colon delimited.  E.g.:
#      mercury	SQUEAKY	App01;App02;App03;App04;App05;App06;App07;App08;App09;App10
#      jeremyq	MOSEY	Client01;Client02;Client03
####
public function WF_load_UID (fname, inout UID_ra[], out UID_racount) {
	auto line, tmpra[], tmpracount, i;
	extern WF_USE_MACHINENAME;
	if (nargs() != 3) return (report_param_msg ("WF_load_UID"));

	eval ("if (WF_USE_MACHINENAME < 0) { eval (\"const WF_USE_MACHINENAME = TRUE;\");}");

	while (getline line < fname) {
		if (substr (line, 1, 1) != "#") {
			tmpracount = split (line, tmpra, "\t");
			#if (((WF_USE_MACHINENAME) ? (tmpra[1] == MACHINENAME) : (tmpra[2] == USERNAME))) {
			if (((WF_USE_MACHINENAME) ? (tmpra[1] == getenv ("computername")) : (tmpra[2] == getenv ("username")))) {
				UID_racount = split (tmpra[3], UID_ra, ";");
				break;
			}
		}
	}
	close (fname);
	if (UID_racount < 1) {
		return E_GENERAL_ERROR;
	} else {
		# Move the array down 1 index, so that the first element is indexed at 0
		for (i = 0; i < UID_racount; i++) {
			UID_ra[i] = UID_ra[i + 1];
		}
		delete UID_ra[UID_racount];
	}
}



####
# Syntax: create_duplicate_GUI_window (win_name, number_of_windows);
# Return value: E_OK
# Parameters:
#    win_name: name of the original GUI window name
#    number_of_windows: the number of copies of the windows
# Description: Create duplicates of the original window in the GUI file.
#    Each of the copies will have numbers from 0 through number_of_windows - 1
#    appended at the end.  The copies will be placed in the temporary GUI file.
#    Use this to build your GUI file's entries for the various WinFrame
#    Sessions.
#    1. Open 1 WinFrame session, e.g. "Application0".
#    2. Use the GUI Edit's learn button to learn the WinFrame Session window.
#       You actually only need to learn the window object, not the menu items.
#       But it doesn't matter.
#    3. Run the create_duplicate_GUI_window function to create the entries for
#       the rest of the WinFrame sessions (e.g. if you plan to use 15 WF sessions):
#         create_duplicate_GUI_window ("Application0", 15);
#       The function will create 15 GUI entries in the temporary GUI file.
#    4. Either Save the temporary GUI file as a new GUI file, or copy the new
#       entries to an existing GUI file, and save it.
####
public function create_duplicate_GUI_window (win_name, number_of_windows) {
	auto win_desc, win_label, win_label1, win_label2, win_name1, win_name2, i, j;
	auto win_desc1, win_desc2, obj_name[], obj_count, GUI_file, obj_desc, obj_num;

	GUI_map_get_desc (win_name, "", win_desc, GUI_file);
	GUI_desc_get_attr (win_desc, "label", win_label);
	win_label = str_replace (win_label, "![0-9]\\*","");


	for (j = 0; j < number_of_windows; j++) {
		win_label1 = win_label & j;
		win_name1 = win_name & j;

		win_desc1 = win_desc;

		GUI_desc_set_attr (win_desc1, "label", win_label1);

		GUI_add (GUI_file, win_name1, "", win_desc1);

		GUI_list_win_objects (GUI_file, win_name, obj_name, obj_num);

		for (i = 1; i <= obj_num; i++) {
			GUI_buf_get_desc (GUI_file, win_name, obj_name[i], obj_desc);
			GUI_add (GUI_file, win_name1, obj_name[i], obj_desc);
			GUI_add (GUI_file, win_name2, obj_name[i], obj_desc);
		}
	}
}

####
# Syntax: check_user_delay ();
# Return values: E_OK;
# Parameters:
#    none
# Description: check if the user needs to delay its action.  This is used to
#    delay a certain user's transaction.  It uses the global variable
#    DELAY_USER and DELAY_USER_TIME.
#      DELAY_USER can be either TRUE or FALSE (1/0).
#      DELAY_USER_TIME is the delay (wait) in seconds.
#    If DELAY_USER is not TRUE, then the DELAY_USER_TIME is ignored.
#    This is useful if you want a certain user to execute it's action
#    several seconds after the others do.  E.g.:
#      DELAY_USER = FALSE;
#      DELAY_USER_TIME = 3;
#      lr_whoami (vuser, sgroup);
#      # Delay Group2 users's action by 3 seconds
#      if (sgroup == "Group2") {
#        eval ("const DELAY_USER = TRUE;");
#      }
#      ...
#      rendezvous ("pre_submit_request");
#      check_user_delay (); # Group2 users will be delayed by 3 seconds here
#      start_transaction ("submit_request");
#      multiple_win_mouse_click (win_names_ra, 200, 250);
#      res = multiple_win_wait_text (win_names_ra, "Received", 60, 20, 325, 50, 380);
#      end_transaction ("submit_request", res);
####
public function check_user_delay () {
	eval ("if (DELAY_USER) {wait (DELAY_USER_TIME);}");
}


####
# Syntax: multiple_win_benchmark (win_names_ra[]);
# Return value: E_OK
# Parameters:
#    win_names_ra[]: array of windows to be included in the benchmark
# Description: Performs a simple set of benchmark to help determine
#    the speed of the current computer in dealing with multiple
#    WF sessions.  This is useful to determine a good DELAY_USER_TIME
#    for a particular machine.
#    To use this, first open how many WinFrame sessions you want to 
#    benchmark.  Do not login yet, but leave the sessions at the WinFrame
#    Logon screen.  Run the multiple_win_benchmark () function with
#    the window arrays as the parameter.
#    If you do not have a login window, make sure all of the windows
#    are at the same screen, and modify the text recognition coordinates.
####
public function multiple_win_benchmark (win_names_ra[]) {
	auto arg_num = nargs(), res, start_time, end_time;
	auto i, win_names_racount, start_time_ra[], end_time_ra[], count, tmp;
	if (arg_num != 1)
		return (report_param_msg ("multiple_win_benchmark"));

	win_names_racount = array_length (win_names_ra);

	output_message ("Benchmarking total window flipping");
	count = 0;
	start_time = get_time ();
	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra [i]);
	}
	end_time = get_time ();
	output_message ("Total time to flip "& win_names_racount &" windows: "& end_time - start_time &" seconds, average: "& (end_time - start_time) / win_names_racount);


	output_message ("Benchmarking text recognition flipping");
	count = 0;
	start_time = get_time ();
	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra [i]);
		###
		# give a good coordinate for text recognition
		###
		res = ((win_get_text (win_names_ra[i], tmp, 261, 265, 282, 279) == E_OK) ? ((tmp == "OK") ? E_OK : E_GENERAL_ERROR) : E_GENERAL_ERROR );
	}
	end_time = get_time ();
	output_message ("Total time for text recognition "& win_names_racount &" windows: "& end_time - start_time &" seconds, average: "& (end_time - start_time) / win_names_racount);

	output_message ("Benchmarking mouse click flipping");
	count = 0;
	start_time = get_time ();
	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra [i]);
		win_mouse_click (win_names_ra[i], 294, 155);
	}
	end_time = get_time ();
	output_message ("Total time to mouse click "& win_names_racount &" windows: "& end_time - start_time &" seconds, average: "& (end_time - start_time) / win_names_racount);
}

####
# Syntax: WF_load_menu_item (fname);
# Return value: default return values
# Parameters:
#    fname: the filename that stores the menu item information.
# Description: Loads the application menu item's coordinate mapping information for the WinFrame.
#    And store it to the array WF_MENU_ITEM_RA[].
#    The function will check the global variable WF_MENU_MOUSE_CLICK to determine
#    whether to load the mouse click coordinate (WF_MENU_MOUSE_CLICK == TRUE)
#    or keyword shortcuts (WF_MENU_MOUSE_CLICK == FALSE).
#
#    The entries in the fname menu map file is as follows:
#
#    # The first entry is the menu item, and the second entry is
#    # the coordinate information, and the third one is key maps
#    # The coordinate is made up of either 1 or 3 items, each 
#    # comma separated:
#    # - The first item is the x coordinate of the main menu item group.
#    # - The second coordinate is the menu item order of the
#    #   menu item.  The first menu item under the menu group, e.g.
#    #   "New" under "File" group has an index of 1 if it is the first
#    #   item under the "File" menu item group.
#    # - The third item is the number of separators that exists to the
#    #   above of the menu item's index location.
#    # From these information, the coordinate of the menu item can be
#    # calculated easily.
#    # The x coordinate can use "$Menu_group_name" to reference the
#    # x coordinate of the menu item group's x-coordinate.  That way
#    # if the x-coordinate changes it is a lot easier to modify.
#    #
#    # The third is the key mapping in case we need to type in the
#    # keys instead of using mouse clicks
#    # *****************************************************************
#    
#    File	33	<kAlt_L-f>
#    File;Switch Application to	$File,11,5	w
#    File;Switch Application to;#1	297,11,5	<kReturn>
#    File;Switch Application to;#2	297,12,5	<kDown_E><kReturn>
#    File;Switch Application to;Reporting		r
#    File;Switch Application to;Business Administration		a
#    File;Switch Application to;Administration		b
#    File;Exit Business Maintenance	$File,12,5	e
#    File;Exit Administration	$File,12,5	e
#    File;Exit Reporting	$File,12,5	e
#    
#    #
#    Feeder	126	<kAlt_L-d>
#    Feeder;Control Panel	$Feeder,1,0	c
#    Feeder;Rejects	$Feeder,2,1	e
#    Feeder;Rejects;Firm	228,2,1	f
####
public function WF_load_menu_item (fname) {
	auto line, tmpra[], tmpracount, i;
	extern WF_MENU_ITEM_RA[], WF_MENU_MOUSE_CLICK;

	if (nargs() != 1) return (report_param_msg ("WF_load_menu_item"));

	eval ("if (WF_MENU_MOUSE_CLICK == \"\") {eval (\"const WF_MENU_MOUSE_CLICK = FALSE;\");}");
	eval ("WF_MENU_ITEM_RA[\"\"] = \"\";");

	for (i in WF_MENU_ITEM_RA) delete WF_MENU_ITEM_RA[i];

	while (getline line < fname) {
		if (substr (line, 1, 1) != "#" && line != "") {
			tmpracount = split (line, tmpra, "\t");
			if (WF_MENU_MOUSE_CLICK) {
				WF_MENU_ITEM_RA[tmpra[1]] = tmpra[2];
			} else {
				WF_MENU_ITEM_RA[tmpra[1]] = tmpra[3];
			}
		}
	}
	close (fname);
}

####
# Syntax: WF_multiple_win_menu_select_item (win_names_ra, menu_item, rendezvous_name, transaction_name);
# Return value: default return values
# Parameters:
#    win_names_ra: array of window names
#    menu_item: the menu item to select, as specified in the WF_load_menu_item () function.
#    rendezvous_name: a rendezvous name to use before selecting the last menu item.  If no rendezvous
#        is required, then leave this blank ("")
#    transaction_name: the transaction name to start after selecting the last menu item.
# Description:  Selects a menu item under WinFrame's PRF.
#    This requires the coordinates of the menu items itself, which is retrieved
#    by the function WF_PRF_load_menu_item ();
#    It will also implement a rendezvous if wanted to right before the last menu item is selected.
#    The function will also start a transaction_name if it is specified after the last menu item is
#    selected.
#    ** Note: This function uses the global variable WF_MENU_MOUSE_CLICK to determine
#             whether to use mouse clicks of key shortcuts to select menu items.
#             if WF_MENU_MOUSE_CLICK is TRUE, then mouse clicks are used, otherwise, key shortcuts are used.
#             By default, WF_MENU_MOUSE_CLICK is FALSE.
####
public function WF_multiple_win_menu_select_item (win_names_ra[], menu_item, rendezvous_name, transaction_name) {
	extern WF_MENU_MOUSE_CLICK;

	if (nargs() != 4) return (report_param_msg ("WF_multiple_win_menu_select_item"));

	# set default WF_MENU_MOUSE_CLICK to FALSE.
	eval ("if (WF_MENU_MOUSE_CLICK == \"\") {eval (\"const WF_MENU_MOUSE_CLICK = FALSE;\");}");

	if (WF_MENU_MOUSE_CLICK) {
		return (WF_multiple_win_menu_select_item_click (win_names_ra, menu_item, rendezvous_name, transaction_name));
	} else {
		return (WF_multiple_win_menu_select_item_type (win_names_ra, menu_item, rendezvous_name, transaction_name));
	}
}


####
# Syntax: WF_multiple_win_menu_select_item_click (win_names_ra[], menu_item, rendezvous_name, transaction_name);
# Return value: default return values
# Parameters:
#    ** see WF_multiple_win_menu_select_item ();
# Description: Selects a menu item using mouse click
####
public function WF_multiple_win_menu_select_item_click (win_names_ra[], menu_item, rendezvous_name, transaction_name) {
	auto win_names_racount, i, j, res_ra[], res_racount;
	auto x1 = 0, x2 = 0, y;
	auto x_ra[], y_ra[], menu_items_ra[], menu_items_racount;
	auto tmpra[], tmpracount, lastmenu = "";
	extern WF_MENU_ITEM_RA[];

	if (nargs() != 4) return (report_param_msg ("WF_multiple_win_menu_select_item_click"));

	if (array_length (WF_MENU_ITEM_RA) < 2) {
		WF_load_menu_item ();
	}

	win_names_racount = array_length (win_names_ra);
	menu_items_racount = split (menu_item, menu_items_ra, ";");

	# These coordinates are w.r.t. the window, not absolute

	# Build the x and y coordinates for the other menu items
	for (i = 0; i < menu_items_racount; i++) {
		tmpracount = split (WF_MENU_ITEM_RA[lastmenu & menu_items_ra[i + 1]], tmpra, ",");
		if (substr (tmpra[1], 1, 1) == "$") {
			x_ra[i] = WF_MENU_ITEM_RA[substr(tmpra[1], 2)];
		} else {
			x_ra[i] = tmpra[1];
		}

		# 28 is the y coordinate of the main menu item.
		# 18 is the number of pixels between 2 menu items
		# 7 is the number of pixels of a horizontal bar
		# To retrieve these values, use WR's win_move_locator_text, and getting the coordinate
		# in the window by using get_win_x ();
		y_ra[i] = 28 + (18 * tmpra[2]) + (7 * tmpra[3]);
		lastmenu = lastmenu & menu_items_ra[i + 1] &";";
	}


	for (j = 0; j < menu_items_racount - 1; j++) {
		for (i = 0; i < win_names_racount; i++) {
			win_activate (win_names_ra[i]);
			win_mouse_click (win_names_ra[i], x_ra[j], y_ra[j]);
		}
	}

	if (rendezvous_name != "") {
		eval ("rendezvous (\""& rendezvous_name &"\");");
		check_user_delay ();
	}

	win_activate (win_names_ra[0]);
	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra[i]);
		win_mouse_click (win_names_ra[i], x_ra[j], y_ra[j]);
		if (transaction_name != "") {
			eval ("start_transaction (\""& transaction_name &"\");");
		}
	}
}

####
# Syntax: WF_multiple_win_menu_select_item_type (win_names_ra[], menu_item, rendezvous_name, transaction_name);
# Return value: default return values
# Parameters:
#    see WF_multiple_win_menu_select_item ();
# Description: selects a menu item using keys typed in (shortcuts)
####
public function WF_multiple_win_menu_select_item_type (win_names_ra[], menu_item, rendezvous_name, transaction_name) {
	auto win_names_racount, i, j, res_ra[], res_racount;
	auto key_ra[], menu_items_ra[], menu_items_racount;
	auto tmpra[], tmpracount, lastmenu = "";
	extern WF_MENU_ITEM_RA[];

	if (nargs() != 4) return (report_param_msg ("WF_PRF_multiple_win_menu_select_item_type"));

	if (array_length (WF_MENU_ITEM_RA) < 2) {
		WF_PRF_load_menu_item ();
	}

	win_names_racount = array_length (win_names_ra);
	menu_items_racount = split (menu_item, menu_items_ra, ";");


	# Build the array of keys to press
	for (i = 0; i < menu_items_racount; i++) {
		key_ra[i] = WF_MENU_ITEM_RA[lastmenu & menu_items_ra[i + 1]];
		lastmenu = lastmenu & menu_items_ra[i + 1] &";";
	}


	for (j = 0; j < menu_items_racount - 1; j++) {
		for (i = 0; i < win_names_racount; i++) {
			win_activate (win_names_ra[i]);
			type (key_ra[j]);
		}
	}

	if (rendezvous_name != "") {
		eval ("rendezvous (\""& rendezvous_name &"\");");
		check_user_delay ();
	}

	win_activate (win_names_ra[0]);
	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra[i]);
		type (key_ra[j]);
		if (transaction_name != "") {
			eval ("start_transaction (\""& transaction_name &"\");");
		}
	}
}



####
# Syntax: multiple_win_type (win_names_ra[], string_to_type);
# Return value: E_OK
# Parameters:
#    win_names_ra: array of window names
#    string_to_type: the string to type
# Description: This types in the specified character to the multiple windows in the
#    win_names_ra array.
####
public function multiple_win_type (win_names_ra[], string_to_type) {
	auto win_names_racount, i;
	if (nargs() != 2) return (report_param_msg ("multiple_win_type"));
	win_names_racount = array_length (win_names_ra);
	for (i = 0; i < win_names_racount; i++) {
		#set_window (win_names_ra[i], 5);
		win_activate (win_names_ra[i]);
		type (string_to_type);
	}
	return E_OK;
}

####
# Syntax: multiple_win_type_delayed (win_names_ra[], string_to_type, pre_type_delay, post_type_delay);
# Return value: E_OK
# Parameters:
#    win_names_ra[]: array of window names
#    string_to_type: the string to type
#    pre_type_delay: delay before typing, in seconds
#    post_type_delay: delay after typing, in seconds
# Description: Types the specified string to each of the windows in the win_names_ra array list,
#    and wait the specified amount of times before and after the type.
####
public function multiple_win_type_delayed (win_names_ra[], string_to_type, pre_type_delay, post_type_delay) {
	auto win_names_racount, i;
	if (nargs() != 4) return (report_param_msg ("multiple_win_type_delayed"));
	win_names_racount = array_length (win_names_ra);
	if (pre_type_delay < 1) pre_type_delay = 0;
	if (post_type_delay < 1) post_type_delay = 0;
	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra[i]);
		wait (pre_type_delay);
		type (string_to_type);
		wait (post_type_delay);
	}
	return E_OK;
}

####
# Syntax: WF_multiple_win_open_window_transaction (win_names_ra[], menu_item, rendezvous_name, transaction_name, max_wait, text_to_wait, x1, y1, x2, y2);
# Return value: default return values
# Parameters:
#    win_names_ra: array of window names
#    menu_item: the menu item in PRF to select (uses WF_MENU_MOUSE_CLICK global variable
#        to determine whether to use mouse clicks or key shortcuts)
#    rendezvous_name: rendezvous name to use (leave blank to skip rendezvous)
#    transaction_name: transaction name to use (leave blank to skip transaction)
#    max_wait: the maximum amount of time to wait
#    text_to_wait: the text to wait for to signify the end of the transaction
#    x1, y1, x2, y2: the rectangular area to look for the text (as recorded using WR's win_get_text's)
# Description:  A transaction that basically opens a window, waits for the text to appears at the
#    specified area, and then closes the window.
#    If no rendezvous or transaction is to be recorded, leave the two parameters as empty strings ("").
####
public function WF_multiple_win_open_window_transaction (win_names_ra[], menu_item, rendezvous_name, transaction_name, max_wait, text_to_wait, x1, y1, x2, y2) {
	auto rc = PASS, res, win_names_racount = array_length (win_names_ra);
	if (nargs() != 10) return (report_param_msg ("WF_multiple_win_open_window_transaction"));

	# Open the window
	rc = PASS;
	WF_multiple_win_menu_select_item (win_names_ra, menu_item, rendezvous_name, transaction_name);
	res = multiple_win_wait_text_WR (win_names_ra, text_to_wait, max_wait, x1, y1, x2, y2);
	if (res != E_OK) rc = FAIL;
	if (transaction_name != "") end_transaction (transaction_name, rc);

	# now close everything
	rendezvous ("pre_close_window");
	multiple_win_type_escape (win_names_ra);
}

####
# Syntax: multiple_win_mouse_click (win_names_ra[], x, y);
# Return value: default return values
# Parameters:
#    win_names_ra: array of window names
#    x, y: coordinate to click on
# Description: Click at the specified coordinate in each of the window
####
public function multiple_win_mouse_click (win_names_ra[], x, y) { #, mouse_button, modifier) {
	auto i, win_names_racount = array_length (win_names_ra);
	#if (nargs() < 4 && nargs() > 5) return (report_param_msg ("multiple_win_mouse_click"));

	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra[i]);
		win_mouse_click (win_names_ra[i], x, y);
	}
}

####
# Syntax: multiple_win_mouse_click_delayed (win_names_ra[], x, y, pre_click_delay, post_click_delay);
# Return value: default return values
# Parameters:
#    win_names_ra: array of window names
#    x, y: coordinate to click on
# Description: Click at the specified coordinate in each of the window
####
public function multiple_win_mouse_click_delayed (win_names_ra[], x, y, pre_click_delay, post_click_delay) {
	auto i, win_names_racount = array_length (win_names_ra);
	if (nargs() != 5) return (report_param_msg ("multiple_win_mouse_click_delayed"));
	if (pre_click_delay < 1) pre_click_delay = 0;
	if (post_click_delay < 1) post_click_delay = 0;

	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra[i]);
		wait (pre_click_delay);
		win_mouse_click (win_names_ra[i], x, y);
		wait (post_click_delay);
	}
}

####
# Syntax: multiple_win_mouse_dbl_click (win_names_ra[], x, y);
# Return value: default return values
# Parameters:
#    win_names_ra: array of window names
#    x, y: coordinate to double click on
# Description: Double click at the specified coordinate in each of the window
####
public function multiple_win_mouse_dbl_click (win_names_ra[], x, y) { #, mouse_button, modifier) {
	auto i, win_names_racount = array_length (win_names_ra);
	#if (nargs() < 4 && nargs() > 5) return (report_param_msg ("multiple_win_mouse_click"));

	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra[i]);
		win_mouse_dbl_click (win_names_ra[i], x, y);
	}
}

####
# Syntax: multiple_win_mouse_dbl_click_delayed (win_names_ra[], x, y, pre_click_delay, post_click_delay);
# Return value: default return values
# Parameters:
#    win_names_ra: array of window names
#    x, y: coordinate to double click on
#    pre_click_delay: delay between the time that the window is activated, and the click
#    post_click_delay: delay after the click
# Description: Double click at the specified coordinate in each of the window
####
public function multiple_win_mouse_dbl_click_delayed (win_names_ra[], x, y, pre_click_delay, post_click_delay) {
	auto i, win_names_racount = array_length (win_names_ra);
	if (nargs() != 5) return (report_param_msg ("multiple_win_mouse_dbl_click_delayed"));
	if (pre_click_delay < 1) pre_click_delay = 0;
	if (post_click_delay < 1) post_click_delay = 0;

	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra[i]);
		wait (pre_click_delay);
		win_mouse_dbl_click (win_names_ra[i], x, y);
		wait (post_click_delay);
	}
}

####
# Syntax: multiple_win_mouse_drag (win_names_ra[], start_x, start_y, end_x, end_y);
# Return value: E_OK
# Parameters:
#    win_names_ra[]: array of window names
#    start_x, start_y: the original drag coordinates
#    end_x, end_y: the ending drag coordinates
# Description: Performs a mouse drag on each of the windows in the win_names_ra array.
####
public function multiple_win_mouse_drag (win_names_ra[], start_x, start_y, end_x, end_y) { #, mouse_button, modifier) {
	auto i, win_names_racount = array_length (win_names_ra);
	if (nargs() != 5) return (report_param_msg ("multiple_win_mouse_drag"));

	for (i = 0; i < win_names_racount; i++) {
		win_activate (win_names_ra[i]);
		win_mouse_drag (win_names_ra[i], start_x, start_y, end_x, end_y);
	}
}


####
# Syntax: WF_arrange_windows ();
# Return value: E_OK
# Parameters:
#    none
# Description: Stacks all of the windows in the staggered tile order
####
public function WF_arrange_windows () {
	eval ("for (i = 0; i < win_names_racount; i++) {win_move (win_names_ra [i], 10 * i, 0);	}");
}

####
# Syntax: WF_win_load (WF_name);
# Return values: default return values
# Parameters:
#    WF_name: name of the WinFrame entry, which should correspond to the
#        logical window name in the GUI file
# Description: Opens the WinFrame session by executing a call to the WinFrame
#    client software.  It then waits for the said window to appears.
#    Modify the path of the application according to your installation.
####
public function WF_win_load (win_name) {
	if (win_exists (win_name) != E_OK) {
		# Modify the paths according to your installation
		return (invoke_application ("M:\\Apps\\Citrix\\WinFrame\\wfcrun32.exe", "\""& win_name &"\" /iniwfclient:prfcli.ini /iniappsrv:PRF.INI", "M:\\Apps\\Citrix\\WinFrame", SW_SHOWNORMAL));
	}
	return (win_wait (win_name, 180));
}

####
# Syntax: array_length (array_name);
# Return value: length of array.
# Parameters:
#    array_name: the name of the array
# Description: Returns the length of an array.
####
public function array_length (in_ra[]) {
	auto counter = 0, i;
	if (nargs != 1)
		return (report_param_msg ("array_length"));

	for (i in in_ra) counter++;
	return counter;
}

####
# Syntax: multiple_win_wait_bitmap (win_names_ra[], bitmap, max_wait, x1, y1, width, height);
# Return value: default return values
# Parameters:
#    win_names_ra: array of window names
#    bitmap: image to wait for
#    max_wait: the maximum amount of seconds to wait
#    x1, y1, width, height: the rectangular area to search for the text
# Description: Waits for a bitmap image to appear in the specified windows.
#    Note: this function has not been widely tested yet as of 1998/11/01 (FF).
####
public function multiple_win_wait_bitmap (win_names_ra[], bitmap, max_wait, x1, y1, width, height) {
	auto arg_num = nargs(), res, start_time, tmp;
	auto i, win_names_racount, res_ra[], x_ra[], y_ra[], count;
	if (arg_num != 7)
		return (report_param_msg ("multiple_win_wait_text_WR - "& arg_num));

	win_names_racount = array_length (win_names_ra);

	count = 0;
	start_time = get_time();
	do {
		for (i = 0; i < win_names_racount; i++) {
			if (res_ra[i] != E_OK) {
				win_activate (win_names_ra[i]);
				res_ra[i] = win_wait_bitmap (win_names_ra[i], bitmap, 0, x1, y1, width, height);
				if (res_ra[i] == E_OK) {
					count++;
				}
			}
		}
	} while (((get_time () - start_time) < max_wait) &&  (count < win_names_racount));
	for (i = 0; i < win_names_racount; i++) {
		if (res_ra[i] != E_OK) error_message ("The image does not appear in window "& win_names_ra[i]);
	}
	return (win_names_racount - count);
}
