CEdit::CharFromPos workaround: almost there ... one more problem 
Author Message
 CEdit::CharFromPos workaround: almost there ... one more problem

Hi groupies,

Ok. I fixed the problem with the calculation of the length of a tab
charachter in pixels. The thing I did not see clearly enough is that when
you call CEdit::SetTabStops, is that you pass the length of a tab charachter
in *dialog units*. I came up with the following solution based on a thorough
browse through documentation and MFC files (note that MYTAB_STOPS is the
width of a tab charachter in dialog units, previously passed to

 CClientDC parent_dc( GetParent() );
 parent_dc.SelectObject( GetParent()->GetFont() );

 // Get size for total alphabet in parent font
 CSize cs_alphabet =
wxyz"), 52);

 // Average of text extents of alphabet, round up
 long average_cx_alphabet = (cs_alphabet.cx + 26) / 52;

 // Convert from dialog units (MYTAB_STOPS) to pixels (tab_pxls)
 long tab_pxls = (MYTAB_STOPS * average_cx_alphabet) / 4;

But after some more testing another problem appeared. Now my calculation of
the line index fails! Darn, just when you think you got all right...
The new problem is the following. When I set the font of the CEdit (using
CWnd::SetFont) to the 'Arial' or 'Times New Roman' font with height 32, the
calculation of the
line number is incorrect. If I set the font of the edit control to 'Tahoma'
or 'MS Sans Serif' or 'Courier' also height 32 (or any other height) the
line number is
correct. The calculation of the line index uses the height of a line of
text, which is calculated as follows:

 CClientDC client_dc(this);
 client_dc.SelectObject( GetFont() );
 TEXTMETRIC text_metric;
 int height_line = text_metric.tmHeight + text_metric.tmExternalLeading; //

This produces incorrect results for the height of a line with 'Arial' or
'Times New Roman' Font. Can someone with can shed some light on this?
Included below are test functions that you can copy/paste into your
subclassed CEdit control, if you wish, and help me find a solution. A
function to set the font is also included (SetTestFont). This also sets the
length to a large value, and sets the tab stops.

Thanks, Tom.

#include <cmath> // floor()

static const int TAB_DLU = 8;

void CEditCtrl::OnRButtonDown(UINT nFlags, CPoint point)
 int beg_sel, end_sel, char_ind = CharFromPos_(point);
 GetSel(beg_sel, end_sel);

 if(beg_sel!=end_sel) // if HasSelection()
  if(char_ind<beg_sel || char_ind>end_sel) // if clicked outside selection
   SetSel(char_ind, char_ind); // then move cursor to clicked position
   ; // else retain selection
  SetSel(char_ind, char_ind); // else move cursor to clicked position

 CEdit::OnRButtonDown(nFlags, point);


// The workaround: incorrect line number for some fonts (
// e.g. Arial or Times New Roman font)
#define DLU2PXL_X_(dlu, factor) ( ( (dlu) * (factor) ) / 4 )
#define DLU2PXL_X(dlu) ( DLU2PXL_X_(dlu,av_cx_alpha) )
UINT CEditCtrl::CharFromPos_(CPoint pt)
 CRect rect;

 UINT char_ind = 0;
  CClientDC parent_dc(GetParent());
  CFont* old_font = parent_dc.SelectObject( GetParent()->GetFont() );
  CSize cs_alpha =
wxyz"), 52);
  int av_cx_alpha = (cs_alpha.cx + 26) / 52;
  int tab_pxls = DLU2PXL_X( TAB_DLU );

  CClientDC client_dc(this);
  CFont* old_font = client_dc.SelectObject( GetFont() );
  TEXTMETRIC text_metric;
  int height_row = text_metric.tmHeight + text_metric.tmExternalLeading;//
  int line_count = GetLineCount();
  WORD left_margin = LOWORD(GetMargins());

  int line_ind = 0;
  if( pt.y != 0 ) // top margin is one pixel
   line_ind = int(GetFirstVisibleLine() + floor((pt.y - 1) /
  if(line_ind > line_count-1) // clip to last line
   line_ind = line_count-1;
  int pos = 0;

  if(pt.x>=int(left_margin)) // clip to left margin
   CString line = GetTextAt(line_ind); // see definition below
    LPCTSTR line_= line;
    int inter_char_pxl = client_dc.GetTextCharacterExtra();
    int x_pos = left_margin; // start at left margin
    for(; x_pos < pt.x && pos != line.GetLength(); ++pos)
     if(line_[pos] == '\t')
      x_pos += tab_pxls - ((x_pos - left_margin) % tab_pxls);
      x_pos += client_dc.GetTextExtent(&line_[pos], 1).cx +

    if(x_pos>=pt.x) // adjust pos
  char_ind = LineIndex(line_ind) + pos;
 return char_ind;


CString CEditCtrl::GetTextAt(int line_index)
 CString ret;
 LPTSTR line_buf = ret.GetBuffer(4096);
 int cc = GetLine((line_index==-1) ? (LineFromChar()) : (line_index),
line_buf, 4096-1);
 return ret;


// Sets the font with name for the CEdit passed.
// Returns a pointer to the font allocated. This
// pointer should still be deleted. And some other
// testing properties
// Call with e.g. "Arial" or "Courier" or "Times New Roman", ...
CFont* SetTestFont(CEdit& edit_ctrl, const CString& font_name)
 lf.lfHeight = 32;
 lf.lfWidth = 0;
 lf.lfEscapement = 0;
 lf.lfOrientation = 0;
 lf.lfWeight = FW_DONTCARE;
 lf.lfItalic = FALSE;
 lf.lfUnderline = FALSE;
 lf.lfStrikeOut = FALSE;
 lf.lfCharSet = DEFAULT_CHARSET;
 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
 lf.lfQuality = DEFAULT_QUALITY;
 lf.lfPitchAndFamily = DEFAULT_PITCH;
 lstrcpy(lf.lfFaceName, font_name);

 CFont* pFont = new CFont();

 edit_ctrl.SetLimitText( 0x7FFFFFFF ); // large
 edit_ctrl.SetTabStops( TAB_DLU ); // tab stops

 return pFont;


Mon, 12 Sep 2005 20:55:33 GMT  
 [ 1 post ] 

 Relevant Pages 

1. Workaround for CEdit::CharFromPos (final!)

2. CEdit::CharFromPos -- trying to write a bugfix, please help (having problems with tabs)

3. CEdit::CharFromPos failing for large text, need help

4. CEdit::CharFromPos

5. CEdit, Round 4 (Almost done)

6. from one CEdit to one another

7. BeforeNavigate workaround problem

8. CRichEditCtrl & CharFromPos

9. CharFromPos and GetCaretPos usage

10. Multiple CEdit-Views with one scrollbar

11. CEdit box ** CEdit box ** CEdit box

12. no problem with the debug version but problem with the release one


Powered by phpBB® Forum Software