/*------------------------------------------------------------------------------*
 * File Name: OMat.c															*
 * Creation: GJL 5/13/02														*
 * Purpose: Origin C file implementing matrix related dialogs					*
 * Copyright (c) OriginLab Corp.	2002-2007									*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 * SY 12/29/2004 v8.0181 OC_CLEANUP_REMOVING_wks_utils_dlg_utils_app_utils		*
 * Nicole 07/05/2007 v8.0655 REMOVE_SPACE_FOR_LOCATION_STRING					*
 *------------------------------------------------------------------------------*/

//////////////////////////////////////////////////////////////////////////////////
// Included header files
//////////////////////////////////////////////////////////////////////////////////
//
// System includes
#include <Origin.h>       // Origin C constants

// Include needed Origin C utility functions
/// SY 12/29/2004 v8.0181 OC_CLEANUP_REMOVING_wks_utils_dlg_utils_app_utils
//#include "App_Utils.h"
//#include "Wks_Utils.h"
/// end OC_CLEANUP_REMOVING_wks_utils_dlg_utils_app_utils

// Include definitions of OMat constants, non-localized strings, and function prototypes
#include "OMat.h"

////////////////////////////////////////////////////////////////////////////////////

/**
		HLOC function to directly convert an Origin worksheet or Excel workbook range
		selection to a matrix.
	Example:
		See the OnClickConvert function in OMatDlgDMC.c for a sample call.
	Parameters:
		trDMC=Input DirectMatConvert tree containing all dialog settings
	Return:
		Returns DMC_NO_ERROR on success and a DMC_ERROR code on failure.
*/
int ConvertWksToMatrixDirect(Tree& trDMC)
{
	// *** Declare local variables ***
	string strMsg, strWinName;
	int iDataFormat, iReturn;
	MatrixPage mpOut; // Newly created output matrix page

	// *** Combine relevant checkbox items into bitwise variable *** 
	if( trDMC.xVariesAcrossCols.nVal == 1 )
		iDataFormat=1 + 2*trDMC.xInRow1.nVal + 4*trDMC.yInCol1.nVal;
	else
		iDataFormat=0 + 2*trDMC.xInCol1.nVal + 4*trDMC.yInRow1.nVal;

	// *** Create output matrix ***
	strWinName = CreateWindow("Origin.OTM", CREATE_HIDDEN);
	mpOut = Project.MatrixPages(strWinName);
	
	// *** Call main function to convert worksheet/workbook selection range to matrix ***
	iReturn = convert_wks_to_matrix_direct(trDMC.SelRange.strVal, iDataFormat, mpOut);

	// *** Process iReturn error codes ***
	switch( iReturn )
	{
		// Process Escape
		case DMC_ESCAPE:
			if( mpOut.IsValid() )
				mpOut.Destroy();
			return iReturn;
			break;

		// Process no error
		case DMC_NO_ERROR:
			mpOut.SetShow(); // Show matrix window
			return iReturn;
			break;

		// Process input range specification error
		case DMC_WKS_SEL_ERROR:
			if( mpOut.IsValid() )
				mpOut.Destroy();
			strMsg = _L(DMC_WKS_SEL_ERROR_MSG);
			strMsg.Write(WRITE_MESSAGE_BOX); // Else bad selection so give error message
			return iReturn;
			break;

		// Process input worksheet/workbook and pre-output matrix errors
		case DMC_INPUT_WKS_PAGE_ERROR:
		case DMC_INPUT_ORIGIN_WKSHT_ERROR:
		case DMC_INPUT_EXCEL_WKSHT_ERROR:
		case DMC_UPDATE_ORIGIN_ERROR:
			if( mpOut.IsValid() )
				mpOut.Destroy();
			strMsg = _L(DMC_INPUT_WKS_ERROR_MSG);
			strMsg.Write(WRITE_MESSAGE_BOX);
			return iReturn;
			break;

		// Process output matrix errors
		case DMC_CPY_WKS_TO_MAT_ERROR:
		case DMC_TRANSPOSE_MATRIX_ERROR:
		case DMC_ATTACH_OUTPUT_MAT_ERROR:
		case DMC_SUB_MAT_ERROR:
			if( mpOut.IsValid() )
				mpOut.Destroy();
			strMsg = _L(DMC_OUTPUT_MAT_ERROR_MSG);
			strMsg.Write(WRITE_MESSAGE_BOX);
			return iReturn;
			break;
	}

	// *** Process iReturn warning codes if any ***
	if( iReturn & DMC_XMAP_WARNING )
	{
		strMsg = _L(DMC_XMAP_WARNING_MSG);
		strMsg.Write(WRITE_MESSAGE_BOX);
	}
	if( iReturn & DMC_YMAP_WARNING )
	{
		strMsg = _L(DMC_YMAP_WARNING_MSG);
		strMsg.Write(WRITE_MESSAGE_BOX);
	}
	
	// *** Show matrix window ***
	mpOut.SetShow();

	return DMC_NO_ERROR; // Reset error flag after warnings are made
}

///---------- SY 12/29/2004 v8.0181 OC_CLEANUP_REMOVING_wks_utils_dlg_utils_app_utils
// Moved from wks_utils.c

/**
		Create new window using specified template, options, and enumerated base name.
	Example:
		// Create hidden matrix window with no GUI access allowed (From Origin C function)
		string strWinName;
		strWinName = CreateWindow( "Origin.OTM", CREATE_HIDDEN | CREATE_NO_GUI_ACCESS );
		// Create hidden matrix window with no GUI access allowed (From LabTalk script)
		iOption=3 + 131072; // CREATE_HIDDEN | CREATE_NO_GUI_ACCESS == 3 + 0x00020000
		%A=CreateWindow("Origin.OTM",iOption)$;
	Parameters:
		strTemplateName=Input path and name of template to use, default template name is "Origin.OTW",
			if template name is specified with no extension default extension is "OTW"
		iCreateOptions=Input create options as specified by bitwise CREATE_ constants in
			OC_const.h, default is CREATE_HIDDEN (3)  
		strBaseWindowName=Input enumerated base name for created window, default "" automatically
			uses Origin enumerated names like Data1, Data2, Matrix1, Matrix2, Graph1, Graph2, etc.
		bAlwaysEnumerate=Input flag specifying whether or not to enumerate strBaseWindowName
			even if a window by that name does not already exist, default is TRUE 
	Return:
		Returns the name of the newly created window on success and returns "" on failure.  
*/
string CreateWindow( string strTemplateName, int iCreateOptions, string strBaseWindowName,
	 BOOL bAlwaysEnumerate ) // strTemplateName = "Origin.OTW", iCreateOptions = CREATE_HIDDEN, strBaseWindowName = "", bAlwaysEnumerate = TRUE
{
	MatrixPage mpWindow;
	WorksheetPage wpWindow;
	GraphPage gpWindow;
	Page pgWindow;
	string strExt, strWindowName;
	int ii;
	
	strWindowName.Empty();                                      // Init to ""
	
	ii = strTemplateName.Find( '.' );                           // Find . in template name
	if( ii < 0 )                                                // If no . then...
	{
		strExt = "OTW";                                         // Assume extension is OTW
		strTemplateName = strTemplateName + "." + strExt;       // Add . and default extension to template name 
	}
	else                                                        // Else get extension
	{
		strExt =  strTemplateName.Mid(ii + 1);                  // All characters to right of . is extension
		strExt.MakeUpper();                                     // Make upper case to simplify comparison
		if( !strExt.Match("OT?") )                              // If first two characters of extension are not OT return ""
			return strWindowName;
	}

	// Must use derived class to create page and then "cast"
	switch( strExt.GetAt(2) )                                   // First two characters are OT, switch on 3rd character
	{
		// Worksheet template
		case 'W':
			wpWindow.Create( strTemplateName, iCreateOptions ); // Create WorksheetPage
			if( !wpWindow.IsValid() )                           // If WorksheetPage is not valid...
				return strWindowName;                           // Return ""
			pgWindow = (Page) Project.Pages( wpWindow.GetName() ); // Get WorksheetPage as Page (casting does not work)
			break;

		// Graph template
		case 'P':
			gpWindow.Create( strTemplateName, iCreateOptions ); // Create GraphPage
			if( !gpWindow.IsValid() )                           // If GraphPage is not valid...
				return strWindowName;                           // Return ""
			pgWindow = (Page) Project.Pages( gpWindow.GetName() ); // Get GraphPage as Page (casting does not work)
			break;

		// Matrix template
		case 'M':
			mpWindow.Create( strTemplateName, iCreateOptions ); // Create MatrixPage
			if( !mpWindow.IsValid() )                           // If MatrixPage is not valid...
				return strWindowName;                           // Return ""
			pgWindow = (Page) Project.Pages( mpWindow.GetName() ); // Get MatrixPage as Page (casting does not work)
			break;

		// All other cases
		default:
			return strWindowName;                               // Return "" 
			break;
	}
	
	if( pgWindow.IsValid() )                                    // If created window is valid...
	{
		strWindowName = pgWindow.GetName();                     // Get name of created window
		if( !strBaseWindowName.IsEmpty() )                      // If a base window name was specified...
		{
			if( strBaseWindowName.CompareNoCase( strWindowName ) != 0 ) // If base name is not same as the created window...
			{
				if( !Project.Pages( strBaseWindowName ).IsValid() && !bAlwaysEnumerate ) // If window having base name does not exist and user does not want to enumerate
					pgWindow.Rename( strBaseWindowName );       // Don't enumerate, rename window to base name
				else                                            // Else enumerate...
				{						
					for( ii = 1; ii > 0; ii++ )                 // Loop until an enumerated window name does not exist...
					{
						strWindowName.Format( "%s%d", strBaseWindowName, ii ); // Make enumerated window name
						if( !Project.Pages( strWindowName ).IsValid() ) // If window having this name does not exist
						{
							pgWindow.Rename( strWindowName );   // Rename window
							break;                              // Break out of until (for) loop
						}                                       // End window having name
					}                                           // End until loop
				}                                               // End enumerate
			}                                                   // End base name is not same
		}                                                       // End base name was specified
		if( !Project.Pages( strWindowName ).IsValid() )         // If created and possibly renamed window does not exist...
			strWindowName = "";                                 // Return with failure
	}                                                           // End window valid
	
	return strWindowName;                                       // Return newly created window name if successful and "" if not
}

/**
		Parse a worksheet/workbook selection string and return the worksheet/workbook name, workbook sheet
		name (if Excel workbook), and column and row selection indices (0 based offsets, as integers).
	Example:
		See function omConvertWksToMatrixDirect in OMat.c for sample call.
	Parameters:
		strWksSelection=Input worksheet/workbook selection string
		strWksName=Output worksheet/workbook name
		strSheetName=Output workbook sheet name (if Excel workbook, else NULL)
		iC1=Output index of first selected column (0 based)
		iC2=Output index of last selected column (0 based)
		iR1=Output index of first selected row (0 based)
		iR2=Output index of last selected row (0 based)
	Return:
		Returns TRUE, worksheet/workbook name, workbook sheet name (if Excel), and column and row selection
		indices (0 based offsets, as integers) on success and FALSE on failure.
*/
BOOL ParseWksSelection( string strWksSelection, string& strWksName, string& strSheetName, int& iC1, int& iC2, int& iR1, int& iR2 )
{
	int ii;
	string str, strCol, strRow;
	
	strWksName.Empty();                  // Initialize
	strSheetName.Empty();
	iC1=iC2=iR1=iR2=-1;

	// Trim white space
	str = strWksSelection;
	str.TrimLeft();
	str.TrimRight();

	// Excel workbbook format:  str=="[Book1]Sheet1!$A$1:$B$7"
	// Origin worksheet format: str=="Worksheet_A[1]:B[7]"
	if( str.GetAt(0) == '[' )            // If first character is [ assume Excel workbook format
	{
		Worksheet wksBook;
		Worksheet wksSheet;
		string strCheckSheetName;

		// str=="[Book1]Sheet1!$A$1:$B$7"
		str = str.Mid(1);                // Strip off [ character
		
		// str=="Book1]Sheet1!$A$1:$B$7"
		ii = str.Find(']');              // Find ] character
		if( ii < 0 )                     // If no ] return error
			return FALSE;
		strWksName = str.Left(ii);       // Get Excel workbook name
		wksBook.Attach( strWksName );    // Attach to worksheet underlying workbook 
		if( !wksBook.IsValid() )         // If worksheet not valid return error
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off workbook name and ] character
		
		// str=="Sheet1!$A$1:$B$7"
		ii = str.Find('!');              // Find ! character
		if( ii < 0 )                     // If no ! return error
			return FALSE;
		strSheetName = str.Left(ii);     // Get Excel workbook sheet name from input string
		// Get Excel workbook sheet name from internal Origin workbook sheet
		wksSheet = (Worksheet) wksBook.GetPage().Layers( strSheetName ); // Get internal workbook sheet object by name
		if( !wksSheet.IsValid() )        // If worksheet not valid return error
			return FALSE;
		if( !wksSheet.GetName( strCheckSheetName ) ) // Get name of internal workbook sheet, if problem return error
			return FALSE;
		if( strSheetName.CompareNoCase( strCheckSheetName ) ) // If sheet names do not match return error 
			return FALSE;
 		str = str.Mid( ii + 1 );         // Strip off sheet name and ! character

		// str=="$A$1:$B$7"
		ii = str.Find('$');              // Find first $ character
		if( ii != 0 )                    // If $ is not next character return error
			return FALSE;		
		str = str.Mid( ii + 1 );         // Strip off first $ character
		
		// str=="A$1:$B$7"
		ii = str.Find('$');              // Find second $ character
		if( ii < 0 )                     // If no $ return error
			return FALSE;
		strCol = str.Left(ii);           // Get name of first column
		if( !wksSheet.Columns( strCol ).IsValid() ) // If first selected column is not valid return error  
			return FALSE;
		iC1 = wksSheet.Columns( strCol ).GetIndex(); // Get column number of first selected column
		if( iC1 < 0 )                    // If column not valid return error
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off name of first column and second $ character
		
		// str=="1:$B$7"
		ii = str.Find(':');              // Find : character
		if( ii < 0 )                     // If no : return error
			return FALSE;
		strRow = str.Left(ii);           // Get first row number (as string)
		iR1 = atoi( strRow ) - 1;        // Get first row number (as integer, subtract 1 for 0 based offset )
		strRow.Format( "%d", iR1 + 1 );  // Rebuild strRow (as string from integer) for check
		if( strRow.Compare( str.Left(ii) ) ) // If not same return error 
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off first row number and : character
		
		// str=="$B$7"
		ii = str.Find('$');              // Find third $ character		
		if( ii != 0 )                    // If $ is not next character return error
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off third $ character
		
		// str=="B$7"
		ii = str.Find('$');              // Find fourth $ character
		if( ii < 0 )                     // If no $ return error
			return FALSE;
		strCol = str.Left(ii);           // Get name of second column
		if( !wksSheet.Columns( strCol ).IsValid() ) // If second selected column is not valid return error
			return FALSE;
		iC2 = wksSheet.Columns( strCol ).GetIndex(); // Get column number of second selected column
		if( iC2 < 0 )                    // If column not valid return error
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off name of second column and fourth $ character
		
		// str=="7" - Remaining characters are second row number (as string)
		iR2 = atoi( str ) - 1;           // Get second row number (as integer, subtract 1 for 0 based offset)
		strRow.Format( "%d", iR2 + 1 );  // Rebuild strRow (as string from integer) for check
		if( strRow.Compare( str ) )      // If not same return error 
			return FALSE;
		
		// If column numbers are not in correct bounds return error
		if( iC1 < 0 || iC1 > iC2 || iC2 >= wksSheet.GetNumCols() )
			return FALSE;

		// If row numbers are not in correct bounds return error
		if( iR1	< 0 || iR1 > iR2 || iR2 >= wksSheet.GetNumRows() )
			return FALSE;
	}
	else                                 // Else first character not [ assume Origin worksheet format
	{
		Worksheet wksWorksheet;
		
		// str=="Worksheet_A[1]:B[7]"
		ii = str.Find('_');              // Find _ character
		if( ii < 0 )                     // If no _ return error
			return FALSE;		
		strWksName = str.Left(ii);       // Get Origin worksheet name
		wksWorksheet.Attach( strWksName );// Attach to worksheet 
		if( !wksWorksheet.IsValid() )    // If worksheet not valid return error
			return FALSE;		
		str = str.Mid( ii + 1 );         // Strip off worksheet name and _ character

		// str=="A[1]:B[7]"
		ii = str.Find('[');              // Find first [ character
		if( ii < 0 )                     // If no [ return error
			return FALSE;
		strCol = str.Left(ii);           // Get name of first column
		if( !wksWorksheet.Columns( strCol ).IsValid() ) // If first selected column is not valid return error  
			return FALSE;
		iC1 = wksWorksheet.Columns( strCol ).GetIndex(); // Get column number of first selected column
		if( iC1 < 0 )                    // If not valid return error
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off name of first column and first [ character
		
		// str=="1]:B[7]"		
		ii = str.Find(']');              // Find first ] character
		if( ii < 0 )                     // If no ] return error
			return FALSE;
		strRow = str.Left(ii);           // Get first row number (as string)
		iR1 = atoi( strRow ) - 1;        // Get first row number (as integer, subtract 1 for 0 based offset)
		strRow.Format( "%d", iR1 + 1 );  // Rebuild strRow (as string from integer) for check
		if( strRow.Compare( str.Left(ii) ) ) // If not same return error 
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off first row number and first ] character
		
		// str==":B[7]"		
		ii = str.Find(':');              // Find : character
		if( ii != 0 )                    // If : is not next character return error
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off : character

		// str=="B[7]"
		ii = str.Find('[');              // Find second [ character
		if( ii < 0 )                     // If no [ return error
			return FALSE;
		strCol = str.Left(ii);           // Get name of second column
		if( !wksWorksheet.Columns( strCol ).IsValid() ) // If second selected column is not valid return error
			return FALSE;
		iC2 = wksWorksheet.Columns( strCol ).GetIndex(); // Get column number of second selected column
		if( iC2 < 0 )                    // If not valid return error
			return FALSE;
		str = str.Mid( ii + 1 );         // Strip off name of second column and second [ character
		
		// str=="7]"
		ii = str.Find(']');              // Find second ] character
		if( ii < 0 )                     // If no ] return error
			return FALSE;
		strRow = str.Left(ii);           // Get second row number (as string)
		iR2 = atoi( strRow ) - 1;        // Get second row number (as integer, subtract 1 for 0 based offset)
		strRow.Format( "%d", iR2 + 1 );  // Rebuild strRow (as string from integer) for check
		if( strRow.Compare( str.Left(ii) ) )// If not same return error 
			return FALSE;
		str = str.Mid(ii);               // Strip off second row number
		
		// str=="]"
		if( str.Compare( "]" ) )         // If last character is not ] return error
			return FALSE;
		
		// If column numbers are not in correct bounds return error
		if( iC1 < 0 || iC1 > iC2 || iC2 >= wksWorksheet.GetNumCols() )
			return FALSE;
		
		// If row numbers are not in correct bounds return error	
		if( iR1	< 0 || iR1 > iR2 || iR2 >= wksWorksheet.GetNumRows() )
			return FALSE;
	}
	
	return TRUE;
}

/**
		Gets the current selection from an Origin worksheet or Excel workbook in native
		Origin or Excel format. The selected range must be contiguous.
	Example:
		string strSelectedRange;
		int iRet;
		iRet = GetContiguousWksSelection( strSelectedRange );
	Parameters:
		strSelectedRange=Output selected range
		bSelectAll=Returns entire worksheet/workbook sheet as selection range, default is FALSE
	Return:
		If successful returns WKS_UTILS_NO_ERROR and a formatted string containing the
		current Origin worksheet or Excel workbook selection. If bSelectAll is TRUE then
		the entire worksheet is returned as the selection string. If there is a problem
		getting the selection or if the selected range is not contiguous then
		WKS_UTILS_BAD_SEL_ERROR and an empty string are returned. If there is no 
		selection then WKS_UTILS_NO_SEL_WARNING and an empty string are returned.
*/
int GetContiguousWksSelection( string& strSelectedRange, BOOL bSelectAll ) // bSelectAll = FALSE
{
	int iPageType, iSelectionType, iC1, iC2, iR1, iR2;
	string strWindowName, strSheetName, strC1Name, strC2Name;
	PageBase pbActiveWindow;
	Worksheet wksActiveWorksheet;
	Column colN;
	
	waitCursor	wcCursor; // Put up a wait cursor
	
	// Initialize to NULL
	strSelectedRange.Empty();
	strSheetName.Empty();
	
	// Get active window...if not valid...
	pbActiveWindow = Project.Pages();
	if( !pbActiveWindow.IsValid() )
		return GCWS_BAD_SEL_ERROR; // Return an error code	
	
	// Get window name and type
	strWindowName = pbActiveWindow.GetName();
	iPageType = pbActiveWindow.GetType();
	
	// If window is not a worksheet or workbook...
	if( iPageType != EXIST_WKS && iPageType != EXIST_EXTERN_WKS )
		return GCWS_BAD_SEL_ERROR; // Return an error code

	// Get active layer in workbook...if error...
	wksActiveWorksheet = (Worksheet) Project.ActiveLayer();
	if( !wksActiveWorksheet.IsValid() )
		return GCWS_BAD_SEL_ERROR; // Return an error code	

	// If window is an Excel workbook...
	if( iPageType == EXIST_EXTERN_WKS )
	{
		// Update Origin from Excel
		if( !wksActiveWorksheet.UpdateOrigin() )
			return GCWS_BAD_SEL_ERROR; // Return an error code	

		// Get Excel sheet name...if error...
		if( !wksActiveWorksheet.GetName( strSheetName ) )
			return GCWS_BAD_SEL_ERROR; // Return an error code	
	}

	// If user wants entire worksheet/workbook as selection range...
	if( bSelectAll )
	{
		// Simulate entire worksheet/workbook as selected...
		iSelectionType = WKS_SEL_ALL;
		iC1 = 0;
		iC2 = wksActiveWorksheet.GetNumCols() - 1;
		wksActiveWorksheet.GetBounds(iR1, 0, iR2, -1);
	}
	else // Get current worksheet/workbook selection
	{
		iSelectionType = wksActiveWorksheet.GetSelection( iC1, iC2, iR1, iR2 );

		// If there is no selection...
		if( iSelectionType == WKS_SEL_NONE )
			return GCWS_NO_SEL_WARNING; // Else Return a warning code

		// If editing a cell or if the selection is not contiguous...
		if( iSelectionType & WKS_SEL_EDIT || iSelectionType & WKS_SEL_DISCONTIGUOUS )
			return GCWS_BAD_SEL_ERROR; // Return an error code
	}

	// Get first column name...if column is not valid...
	colN.Attach( wksActiveWorksheet, iC1 );
	if( !colN.IsValid() )
		return GCWS_BAD_SEL_ERROR; // Return an error code	
	strC1Name = colN.GetName();
	
	// Get second column name...if column is not valid...
	colN.Attach( wksActiveWorksheet, iC2 );
	if( !colN.IsValid() )
		return GCWS_BAD_SEL_ERROR; // Return an error code	
	strC2Name = colN.GetName();

	if( iPageType == EXIST_EXTERN_WKS ) // If Excel workbook format selection range as such
		strSelectedRange.Format( GCWS_EXCEL_CONTIG_SEL, strWindowName, strSheetName, strC1Name, iR1 + 1, strC2Name, iR2 + 1 );
	else                 // Else format selection range as Origin worksheet
		strSelectedRange.Format( GCWS_WKS_CONTIG_SEL, strWindowName, strC1Name, iR1 + 1, strC2Name, iR2 + 1 );
	
	return GCWS_NO_ERROR; // Return no error and good selected range string
}

/**
		Determine whether or not the specified elements of a vectorbase derived object are uniformly
		spaced.
	Example:
		See function omConvertWksToMatrixDirect in OMat.c for sample call.
	Parameters:
		dDelta=Output difference between elements if uniform spacing else 0.0
		vIn=Input vectorbase derived object
		dTol=Input relative tolerance between 0 and 1 (default is 0.05)
		i1=Input beginning 0 based index of vector to test (default is 0)
		i2=Input ending 0 based index of vector to test (default -1 tests to last element)
	Return:
		Returns TRUE and NANUM if only one element is tested, returns TRUE and the difference between the first
		two elements tested if the spacing is uniform, returns FALSE and the difference between the first two
		elements tested if the spacing is not uniform, or returns FALSE and NANUM if there is an error.
*/
BOOL VectorHasUniformSpacing( double& dDelta, vectorbase& vIn, double dTol, int i1, int i2 ) // dTol = 0.05, i1 = 0, i2 = -1
{
	int ii;
	double dDeltaLo, dDeltaHi, dDeltaCur;

	dDelta = NANUM;                             // Initialized value
	
	if( i2 == -1 )                              // If default i2 loop to end of vector
		i2 = vIn.GetSize() - 1;
	
	if( dTol < 0.0 || dTol > 1.0 )              // If relative tolerance is not between 0 and 1 return FALSE and NANUM
		return FALSE;
	
	if( i1 < 0 || i2 < i1  || i2 >= vIn.GetSize() ) // If vector indices are illogical return FALSE and NANUM
		return FALSE;

	if( i1 == i2 )                              // If only testing one element return TRUE and NANUM
		return TRUE;
	
	dDelta = vIn[i1] - vIn[ i1 + 1 ];           // Get delta between first two elements
	if( dDelta < 0 )                            // If dDelta is negative...
	{
		dDeltaLo = ( 1.0 + dTol ) * dDelta;     // Compute lower absolute dDelta from relative tolerance
		dDeltaHi = ( 1.0 - dTol ) * dDelta;     // Compute higher absolute dDelta from relative tolerance
	}
	else                                        // Else if dDelta is not negative...
	{
		dDeltaLo = ( 1.0 - dTol ) * dDelta;     // Compute lower absolute dDelta from relative tolerance
		dDeltaHi = ( 1.0 + dTol ) * dDelta;     // Compute higher absolute dDelta from relative tolerance
	}

	for( ii = i1 + 1; ii < i2; ii++ )           // For each element in specified range of vector...
	{
		dDeltaCur = vIn[ii] - vIn[ ii + 1 ];    // Get absolute delta between current and next element 
		if( dDeltaCur < dDeltaLo || dDeltaCur > dDeltaHi ) // If current absolute delta is not within tolerance... 
			return FALSE;                       // Elements of vector not uniformly spaced
	}
	
	return TRUE;                                // Elements of vector are uniformly spaced (within tolerance)
}
///---------- end OC_CLEANUP_REMOVING_wks_utils_dlg_utils_app_utils

/**
		LLOC function to directly convert an Origin worksheet or Excel workbook range
		selection to a matrix according to a bitwise data format description code.
	Example:
		See the ConvertWksToMatrixDirect function in OMat.c for a sample call.
	Parameters:
		strWksSelection=Input worksheet/workbook range selection string like "Worksheet_A[1]:B[7]" or
			"[Book1]Sheet1!$A$1:$B$7"
		iDataFormat=Bitwise data format description: Bit 0=1 X varies across columns or Bit 0=0 Y varies
			across columns, Bit 1=1 First row contains X/Y values, Bit 2=1 First column contains X/Y values
		mpOut=Output matrix page
	Return:
		Returns DMC_NO_ERROR and a valid matrix page on success or a DMC_ERROR code on failure.
*/
int convert_wks_to_matrix_direct(string strWksSelection, int iDataFormat, MatrixPage& mpOut)
{
	string strWksName, strSheetName;
	int iC1, iC2, iR1, iR2, ii, jj, iReturn;
	double dDelta;
	vector vXMap, vYMap;
	WorksheetPage wpIn;
	Worksheet wksIn;
	PageBase pbActiveWindow;
	matrix mTemp;
	Matrix matOut;
	MatrixLayer mlOut;
	
	waitCursor	wcCursor; // Put up a wait cursor and allow escape (on Esc key)
	
	iReturn = DMC_NO_ERROR;
	
	// *** Parse input worksheet/workbook range selection string ***
	if( !ParseWksSelection(strWksSelection, strWksName, strSheetName, iC1, iC2, iR1, iR2) ) 
		return DMC_WKS_SEL_ERROR;

	// *** Attach to input worksheet/workbook page containing selected data range ***
	wpIn = Project.WorksheetPages(strWksName);
	if( !wpIn.IsValid() )
		return DMC_INPUT_WKS_PAGE_ERROR;
	
	// *** Attach to Origin worksheet layer or Excel workbook sheet containing selected data range ***
	if( strSheetName.IsEmpty() )
	{
		// If Origin worksheet...
		wksIn = (Worksheet) wpIn.Layers(); // Get Origin worksheet object (by default)
		if( !wksIn.IsValid() )
			return DMC_INPUT_ORIGIN_WKSHT_ERROR;
	}
	else
	{
		// Else Excel workbook...
		wksIn = (Worksheet) wpIn.Layers(strSheetName); // Get Excel workbook sheet object (by name)
		if( !wksIn.IsValid() )
			return DMC_INPUT_EXCEL_WKSHT_ERROR;
		// Update Origin from Excel
		if( !wksIn.UpdateOrigin() )
			return DMC_UPDATE_ORIGIN_ERROR;
	}
	
	if( wcCursor.CheckEsc() )
		return DMC_ESCAPE; // Quit if user hits Esc key
	
	// *** Copy worksheet/workbook range to a temporary Origin C matrix ***
	if( !mTemp.CopyFromWks(wksIn, iC1, iC2, iR1, iR2) )
		return DMC_CPY_WKS_TO_MAT_ERROR;
	
	if( wcCursor.CheckEsc() )
		return DMC_ESCAPE; // Quit if user hits Esc key

	// *** If Y varies across columns transpose matrix ***
	if( !(iDataFormat & 1) )
		if( !mTemp.Transpose() )
			return DMC_TRANSPOSE_MATRIX_ERROR;

	if( wcCursor.CheckEsc() )
		return DMC_ESCAPE; // Quit if user hits Esc key

	// *** Get X-column and Y-row maps into vectors ***
	ii = 0; // Assume first row of matrix does not contain X Map
	jj = 0; // Assume first column of matrix does not contain Y Map
	if( iDataFormat & 2 )
	{
		// If first row of matrix contains X map...
		ii = 1; // Set flag to remove row 0 with GetSubMatrix
		mTemp.GetRow(vXMap, 0); // Copy first row of matrix into vector
	}
	if( iDataFormat & 4 )
	{
		// If first column of matrix contains Y map...
		jj = 1; // Set flag to remove column 0 with GetSubMatrix
		mTemp.GetColumn(vYMap, 0); // Copy first column of matrix into vector
	}

	if( wcCursor.CheckEsc() )
		return DMC_ESCAPE; // Quit if user hits Esc key	

	// *** Get active internal Origin matrix layer, then attach to OriginC Matrix object ***
	if( !mpOut.IsValid() )
		return DMC_ATTACH_OUTPUT_MAT_ERROR;
	mlOut = (MatrixLayer) mpOut.Layers();
	if( !mlOut.IsValid() )
		return DMC_ATTACH_OUTPUT_MAT_ERROR;
	matOut.Attach(mlOut);
	if( !matOut.IsValid() )
		return DMC_ATTACH_OUTPUT_MAT_ERROR;
	
	// *** Copy temporary matrix to output matrix removing XY data if needed ***
	if( !mTemp.GetSubMatrix(matOut, jj, -1, ii, -1) )
		return DMC_SUB_MAT_ERROR;
	
	if( wcCursor.CheckEsc() )
		return DMC_ESCAPE; // Quit if user hits Esc key

	// *** Assign X-column map ***
	matOut.SetXMin(1); // Assume a 1 to NumCols X-column map
	matOut.SetXMax(matOut.GetNumCols());
	if(ii)
	{
		// If first row of matrix contained X map...
		if( VectorHasUniformSpacing(dDelta, vXMap, DMC_UNIFORM_SPACING_TOL, jj) )
		{
			// If X map has uniform spacing (within tolerance)...
			matOut.SetXMin(vXMap[jj]); // If first column was Y map use jj=1 else use jj=0 as min X
			matOut.SetXMax(vXMap[vXMap.GetSize() - 1]); // Always use last element as max X
		}
		else
			iReturn += DMC_XMAP_WARNING;
	}
	if( wcCursor.CheckEsc() )
		return DMC_ESCAPE; // Quit if user hits Esc key
	
	// *** Assign Y-row map ***
	matOut.SetYMin(1); // Assume 1 to NumRows Y-row map
	matOut.SetYMax(matOut.GetNumRows());
	if(jj)
	{
		// If first column of matrix contained Y map...
		if( VectorHasUniformSpacing(dDelta, vYMap, DMC_UNIFORM_SPACING_TOL, ii) )
		{
			// If Y map has uniform spacing (within tolerance)...
			matOut.SetYMin(vYMap[ii]); // If first row was X map use ii=1 else use ii=0 as min Y
			matOut.SetYMax(vYMap[vYMap.GetSize() - 1]); // Always use last element as max Y
		}
		else
			iReturn += DMC_YMAP_WARNING;
	}
	if( wcCursor.CheckEsc() )
		return DMC_ESCAPE; // Quit if user hits Esc key

	// *** Set page label and Show (activate) internal Origin matrix page ***
	strWksSelection.TrimLeft();
	strWksSelection.TrimRight();
	
	/// Nicole 07/05/2007 v8.0655 REMOVE_SPACE_FOR_LOCATION_STRING
	//mpOut.Label = _L(DMC_MATRIX_PAGE_LABEL) + strWksSelection; // Set matrix page label
	mpOut.Label = _L(DMC_MATRIX_PAGE_LABEL) + " " + strWksSelection; // Set matrix page label
	/// End REMOVE_SPACE_LOCATION_STRING
	LabTalk.Page.title=3; // Display both matrix name and label

	return iReturn;
}

/**
		Create and initialize a DirectMatConvert tree.
	Return:
		Returns a newly created DirectMatConvert tree.
*/
Tree CreateDirectMatConvertTree()
{
	// *** Create tree and nodes ***
	Tree trDMC;
	trDMC.Reset();
	trDMC.AddNode("SelRange");
	trDMC.AddNode("xVariesAcrossCols");
	trDMC.AddNode("xInRow1");
	trDMC.AddNode("yInCol1");
	trDMC.AddNode("xInCol1");
	trDMC.AddNode("yInRow1");

	// *** Initialize node values ***
	InitDirectMatConvertTree(trDMC);

	return trDMC;
}

/**
		Initialize a DirectMatConvert tree.
	Parameters:
		trDMC=DirectMatConvert tree to initialize
	Return:
		Returns an initialized DirectMatConvert tree by reference.
*/
BOOL InitDirectMatConvertTree(Tree& trDMC)
{
	// *** Set tree nodes ***
	trDMC.SelRange.strVal = "";
	trDMC.xVariesAcrossCols.nVal = 1;
	trDMC.xInRow1.nVal = 0;
	trDMC.yInCol1.nVal = 0;
	trDMC.xInCol1.nVal = 0;
	trDMC.yInRow1.nVal = 0;

	return TRUE;
}

/**
		HLOC function to perfrom conditional replace on an internal Origin matrix.
	Example:
		See the OnClickReplace function in OMatDlgMR.c for a sample call.
	Parameters:
		trMR=Input MatrixReplace tree containing all dialog settings
	Return:
		Returns MR_NO_ERROR or a MR_ERROR code on failure.
*/
int MatrixReplace(Tree& trMR)
{
	PageBase pbActiveWindow;
	MatrixLayer ml;
	Matrix mat;
	WorksheetPage wpUndo;
	Worksheet wksUndo;
	Column colUndo;
	Dataset dsUndo;
	int iUndoColNum;
	uint wBitwiseOption;
	BOOL bUndo;
	string strMatrixName;
	
	waitCursor wcCursor;                            // Put up an hour glass
	bUndo = FALSE;                                  // Init to FALSE

	// *** Get bitwise option value ***
	wBitwiseOption = trMR.ConditionSign.nVal + 1;              // nVal (adjusted for 0 based offeset) is same as wBitwiseOption
	if(wBitwiseOption>6) wBitwiseOption+=2;                    // Except must add 2 if Choice > 6 to set Bit 3 for absolute value
	wBitwiseOption += 16 * trMR.ReplaceSign.nVal;              // And must add 16 to keep original sign when replacing 
	wBitwiseOption += 32 * trMR.ReplaceMissingValue.nVal;      // And must add 32 to set to missing value when not replacing

	// *** If using abs value function with negative or missing condition value in Condition tell user and return *** 
	if( ( wBitwiseOption & 8 ) && ( trMR.ConditionValue.dVal < 0 ) )
	{
		MessageBox(NULL, _L(MR_ABS_COND_ERROR_MSG), _L(MR_TITLE), MB_OK | MB_ICONEXCLAMATION);
		return MR_ABS_COND_ERROR;
	}		

	// *** Get active matrix ***
	pbActiveWindow = Project.Pages();               // Get active page (window)
	if( !pbActiveWindow.IsValid() )                 // If not valid...
	{
		MessageBox(NULL, _L(MR_ACT_MAT_ERROR_MSG), _L(MR_TITLE), MB_OK | MB_ICONSTOP);
		return MR_ACT_MATRIX_ERROR;                 // Output error message and return error code
	}
	
	if( pbActiveWindow.GetType() != EXIST_MATRIX )  // If window is not a matrix...
	{
		MessageBox(NULL, _L(MR_ACT_MAT_ERROR_MSG), _L(MR_TITLE), MB_OK | MB_ICONSTOP);
		return MR_ACT_MATRIX_ERROR;                 // Output error message and return error code
	}
	
	strMatrixName = pbActiveWindow.GetName();       // Get name of active matrix

	ml = (MatrixLayer) Project.ActiveLayer();       // Get active matrix layer
	if( !ml.IsValid() )                             // If not valid...
	{
		MessageBox(NULL, _L(MR_ACT_MAT_ERROR_MSG), _L(MR_TITLE), MB_OK | MB_ICONSTOP);
		return MR_ACT_MATRIX_ERROR;                 // Output error message and return error code
	}

	mat.Attach(ml);                                 // Attach to matrix
	if( !mat.IsValid() )                            // If not valid...
	{
		MessageBox(NULL, _L(MR_ACT_MAT_ERROR_MSG), _L(MR_TITLE), MB_OK | MB_ICONSTOP);
		return MR_ACT_MATRIX_ERROR;                 // Output error message and return error code
	}
	
	// *** Save Original matrix values in Matrix Replace Undo worksheet column ***
	wpUndo = Project.WorksheetPages(MR_UNDO_WKS_NAME);      // Attach to worksheet page
	if( !wpUndo.IsValid() )                         // If not valid (page does not exist) then create
	{
		wpUndo.Create("0", CREATE_HIDDEN);          // Create hidden worksheet from no template
		wpUndo.Rename(MR_UNDO_WKS_NAME);                    // Rename worksheet page MRUndo
	}

	wksUndo = (Worksheet) wpUndo.Layers();          // Attach to worksheet (layer)
	if( !wksUndo.IsValid() )                        // If not valid...
	{
		if( MessageBox(NULL, _L(MR_UNDO_WARNING_MSG), _L(MR_TITLE), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDNO )
			return MR_NO_UNDO_QUIT_ERROR;           // Ask user to continue and return if answer is No
	}
	else
	{
		colUndo = wksUndo.Columns(strMatrixName);   // Attach to column for active matrix
		if( !colUndo.IsValid() )                    // If not valid (column does not exist)...
			iUndoColNum = wksUndo.AddCol(strMatrixName);// Add column and get column number
		else
			iUndoColNum = colUndo.GetIndex();       // Else just get column number
		
		dsUndo.Attach(wksUndo, iUndoColNum);        // Attach Dataset to Undo column
		if( !dsUndo.IsValid() )                     // If not valid...
		{
			if( MessageBox(NULL, _L(MR_UNDO_WARNING_MSG), _L(MR_TITLE), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDNO )
				return MR_NO_UNDO_QUIT_ERROR;       // Ask user to continue and return if answer is No
		}
		else
		{
			if( !mat.GetAsVector(dsUndo) )          // Copy matrix to Undo Dataset...if not valid...
			{
				if( MessageBox(NULL, _L(MR_UNDO_WARNING_MSG), _L(MR_TITLE), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDNO )
					return MR_NO_UNDO_QUIT_ERROR;           // Ask user to continue and return if answer is No
			}
			else
				bUndo = TRUE;                       // Enable restorative Undo
		}
	}

	// *** Replace matrix values ***
	if( !mat.Replace(trMR.ConditionValue.dVal, trMR.ReplaceValue.dVal, wBitwiseOption) ) // If matrix replace fails...
	{
		if( bUndo ) // If Undo is enabled...
		{
			if( !mat.SetByVector(dsUndo) )		// If Undo Matrix Replace is successful...
			{
				MessageBox(NULL, _L(MR_FAILED_UNDO_ERROR_MSG), _L(MR_TITLE), MB_OK | MB_ICONSTOP); // Output an error message
				return MR_FAILED_ERROR;             // And return error code
			}
		}

		// Else, if there is no Undo or if Undo is not successful...
		MessageBox(NULL, _L(MR_FAILED_ERROR_MSG), _L(MR_TITLE), MB_OK | MB_ICONSTOP); // Output an error message
		return MR_FAILED_ERROR;                 	// And return error code
	}
	
	return MR_NO_ERROR;                             // Everything went well
}

/**
		HLOC to undo matrix replace.
	Example:
		iRet = UndoMatrixReplace();
	Return:
		Returns MR_NO_ERROR on success or MR_UNDO_FAILED_ERROR on failure.
*/
int UndoMatrixReplace()
{
	PageBase pbActiveWindow;
	MatrixLayer ml;
	Matrix mat;
	WorksheetPage wpUndo;
	Worksheet wksUndo;
	Column colUndo;
	Dataset dsUndo;
	string strMatrixName;
	int iColNum;

	waitCursor wcCursor; // Put up an hour glass

	pbActiveWindow = Project.Pages(); // Get active page
	if( pbActiveWindow.IsValid() )
	{
		// If page is valid...
		if( pbActiveWindow.GetType() == EXIST_MATRIX )
		{
			// If page type is matrix...
			strMatrixName = pbActiveWindow.GetName(); // Get matrix name

			ml = (MatrixLayer) Project.ActiveLayer(); // Get active matrix layer
			if( ml.IsValid() )
			{
				// If matrix layer is valid...
				mat.Attach(ml); // Attach to matrix
				if( mat.IsValid() )
				{
					// If matrix is valid...
					wpUndo = Project.WorksheetPages(MR_UNDO_WKS_NAME); // Get Undo worksheet page
					if( wpUndo.IsValid() )
					{
						// If worksheet page is valid...
						wksUndo = (Worksheet) wpUndo.Layers(); // Get Undo worksheet (layer)
						if( wksUndo.IsValid() )
						{
							// If worksheet is valid...
							colUndo = wksUndo.Columns(strMatrixName); // Get Undo column
							if( colUndo.IsValid() )
							{
								// If column is valid...
								dsUndo.Attach(colUndo); // Get Undo dataset
								if( dsUndo.IsValid() )
								{
									// If dataset is valid...
									if( dsUndo.GetSize() == mat.GetNumRows() * mat.GetNumCols() )
									{
										// If dataset has same number of cells as matrix...
										if( !mat.SetByVector(dsUndo) )
										{
											// Refresh page before message box
											ml.GetPage().Refresh(TRUE);
											
											// If Undo Matrix Replace is successful...
											iColNum = colUndo.GetIndex(); // Get index of undo column
											if( iColNum > 0 )
												wksUndo.DeleteCol(iColNum); // Delete column if found
											
											MessageBox(NULL, _L(MR_UNDO_SUCCESS_MSG), _L(MR_TITLE), MB_OK | MB_ICONASTERISK); 
											return MR_NO_ERROR; // Success!
										}	// End Undo Matrix Replace is successful
									}	// End same number of cells
								}	// End dataset is valid
							}	// End column is valid
						}	// End worksheet is valid
					}	// End worksheet page is valid
				}	// End matrix is valid
			}	// End matrix layer is valid
		}	// End page type is matrix
	}	// End page is valid
	
	// Else (for all ifs)...	
	MessageBox(NULL, _L(MR_UNDO_FAILED_ERROR_MSG), _L(MR_TITLE), MB_OK | MB_ICONSTOP);
	return MR_UNDO_FAILED_ERROR; // Failure
}	// End Undo Matrix Replace

/**
		Create and initialize a MatrixReplace tree.
	Return:
		Returns a newly created MatrixReplace tree.
*/
Tree CreateMatrixReplaceTree()
{
	// *** Create tree and nodes ***
	Tree trMR;
	trMR.Reset();
	trMR.AddNode("ConditionSign");
	trMR.AddNode("ConditionValue");
	trMR.AddNode("ReplaceValue");
	trMR.AddNode("ReplaceSign");
	trMR.AddNode("ReplaceMissingValue");

	// *** Initialize node values ***
	InitMatrixReplaceTree(trMR);

	return trMR;
}

/**
		Initialize a MatrixReplace tree.
	Parameters:
		trMR=MatrixReplace tree to initialize
	Return:
		Returns an initialized MatrixReplace tree by reference.
*/
BOOL InitMatrixReplaceTree(Tree& trMR)
{
	// *** Set tree nodes ***
	trMR.ConditionSign.nVal = 1;
	trMR.ConditionValue.dVal = NANUM;
	trMR.ReplaceValue.dVal = 0.0;
	trMR.ReplaceSign.nVal = 0;
	trMR.ReplaceMissingValue.nVal = 0;

	return TRUE;
}