CMarkup GetAttribOffsets Method

bool CMarkup::GetAttribOffsets(
  MCD_CSTR szAttrib,
  int* pnStart,
  int* pnLen,
  int* pnInStart = NULL,
  int* pnInLen = NULL
  ) const;

CMarkup Developer License

GetAttribOffsets is only in CMarkup Developer and the free XML editor  FOAL C++ scripting.

See the GetOffsets method for details of how document offsets work. GetAttribOffsets allows you to obtain the offsets into the document text for the specified attribute.

The following XML document has an attribute in the NAME element.

<TEST>
  <NAME code="560">John</NAME>
</TEST>

The last two arguments are optional; they are for obtaining the location of the value inside the quotes. The following example gets the starting offset and length of the code attribute and the starting offset and length of the value 560.

int nStart, nLength, nInStart, nInLength;
xml.ResetPos();
xml.FindChildElem();
xml.IntoElem();
xml.GetAttribOffsets( "code", &nStart, &nLength, &nInStart, &nInLength );
// now nStart is 16, nLength is 10, nInStart is 22, nInLength is 3

Set selection in an edit control

Say you have a CEdit control containing the XML document text of the above example. The following code sets the m_edit control edit selection to the code attribute value; i.e. it highlights 560 in the edit control.

int nStart, nLength, nInStart, nInLength;
CString csDoc;
CMarkup xml;
int nStart, nLength;
m_edit.GetWindowText( csDoc );
if ( xml.SetDoc() && xml.FindChildElem() && xml.IntoElem() )
{
  if ( xml.GetAttribOffsets("code",&nStart,&nLength,&nInStart,&nInLength) )
    m_edit.SetSel( nInStart, nInStart + nInLength );
}

Determine element and attribute at cursor

A more useful example application might provide the user with a function to extend the edit selection to the bounds of the current element or to toggle the selection between the start and end tags of the current element, or even to provide context sensitive help. In these cases, the current element is determined from the cursor location in the edit control. The following example extends the edit selection to the bounds of the current element by first locating the element position using the cursor offset in the document.

CString csDoc;
CMarkup xml;
int nStart, nLength, nEndChar;
m_edit.GetWindowText( csDoc );
m_edit.GetSel( nStart, nEndChar ); // get cursor offset in edit control
xml.SetDoc( csDoc );
FindOffset( xml, nStart ); // locate corresponding position in xml
xml.GetOffsets( &nStart, &nLength );
m_edit.SetSel( nStart, nStart + nLength );

The example code above uses a function called FindOffset provided below. This is a function from the firstobject XML Editor which uses the Offset functions and a navigation algorithm to find the element at the given offset. This function will also indicate if the given offset is in an attribute by returning a one-based attribute position although this feature is not utilized by the above example.

int FindOffset( CMarkup& xml, int nOffset )
{
  // Walk XML to find nOffset
  // If in attribute n, return n+1, if in element return -1
  int nStart, nLen, nInStart, nInLen;
  BOOL bRootFound = FALSE;
  xml.ResetPos();
  while ( xml.FindElem() )
  {
    bRootFound = TRUE;

    // In this element?
    xml.GetOffsets( &nStart, &nLen, &nInStart, &nInLen );
    if ( nOffset < nStart )
      return -1;
    else if ( nOffset >= nStart && nOffset < nStart + nLen )
    {
      // Inside start tag?
      if ( nOffset < nInStart )
      {
        // Is it in an attribute?
        int nAttrib = 0;
        CString csAttrib;
        while ( ! (csAttrib=xml.GetAttribName(nAttrib)).IsEmpty() )
        {
          xml.GetAttribOffsets( csAttrib, &nStart, &nLen );
          if ( nAttrib == 0 && nStart > nOffset )
            return -1; // before first attribute
          if ( nStart + nLen > nOffset )
            return nAttrib + 1;
          ++nAttrib;
        }
        return -1; // after attributes (if any)
      }
      else if ( nOffset >= nInStart + nInLen )
        return -1; // end tag of current element
      else
        xml.IntoElem();
    }
  }
  if ( ! bRootFound )
    return 0;
  if ( xml.GetTagName().IsEmpty() )
    xml.OutOfElem();
  return -1;
}