Workaround for CEdit::CharFromPos (final!) 
Author Message
 Workaround for CEdit::CharFromPos (final!)

Hi groupies,

I wrote a workaround for CEdit::CharFromPos (VC++6SP5), because it can
only return charachter indices below 65536 (as a WORD). The workaround
can return correct charachter indices for CEdit's with large amouts of text

Quote:
>65535 charachters (as a UINT).

The workaround is named CharFromPosEx. It also involves trapping
EM_SETTABSTOPS to store the tab stops set, because CEdit doesn't have a
method like GetTabStops.
You can test the workaround's behaviour by adding the handler for
WM_RBUTTONDOWN, which sets the caret on right clicking.

Yt,
Tom T.
PS: Thanks Rajaraman for your hint.
PS2: Any further remarks are still welcome of course ;)

// HEADER
class CTTEdit : public CEdit
{
public:
 CTTEdit();
 UINT CharFromPosEx(CPoint pt);
 CString GetTextAt_(int line_index=-1);
// ...
protected:
 //{{AFX_MSG(CTTEdit)
 afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
 //}}AFX_MSG
 afx_msg LRESULT OnSetTabStops(WPARAM wParam, LPARAM lParam);
 DECLARE_MESSAGE_MAP()
// ...
private:
 unsigned int tab_stops;
// ...

Quote:
};

// IMPLEMENTATION
#include <cmath> // for floor

// convert dialog units 2 pixels (cx)
#define DLU2PXL_X(dlu, factor) (((dlu)*(factor))/4)

#define DEF_TAB_DLU 32

BEGIN_MESSAGE_MAP(CTTEdit, CEdit)
 //{{AFX_MSG_MAP(CTTEdit)
 ON_WM_RBUTTONDOWN()
 //}}AFX_MSG_MAP
 ON_MESSAGE(EM_SETTABSTOPS, OnSetTabStops )
END_MESSAGE_MAP()

CTTEdit::CTTEdit()
:  tab_stops(DEF_TAB_DLU)
{/* ... */}

// Returns charachter index at point
UINT CTTEdit::CharFromPosEx(CPoint pt)
{
 CRect rect;
 GetClientRect(&rect);

 UINT char_ind = 0;
 if(rect.PtInRect(pt))
 {
  CClientDC parent_dc(GetParent());
  CFont* old_font = parent_dc.SelectObject( GetParent()->GetFont() );
  CSize cs_alpha =
parent_dc.GetTextExtent(_T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv
wxyz"), 52);
  int av_cx_alpha = (cs_alpha.cx + 26) / 52;
  int tab_pxls = DLU2PXL_X(tab_stops, av_cx_alpha);
  parent_dc.SelectObject(old_font);

  CClientDC client_dc(this);
  old_font = client_dc.SelectObject( GetFont() );
  int height_row = client_dc.GetTextExtent(" ").cy;
  int line_count = GetLineCount();
  int left_margin = static_cast<int>( LOWORD(GetMargins()) );

  int line_ind = 0;
  if(pt.y != 0)
  {
   line_ind = static_cast<int>( GetFirstVisibleLine() +
floor((pt.y-1)/double(height_row)) );
   if(line_ind > line_count-1)
    line_ind = line_count-1;
  }
  int pos = 0;

  if(pt.x >= left_margin)
  {
   CString line = GetTextAt_(line_ind);
   if(!line.IsEmpty())
   {
    pt.x -= left_margin;
    LPCTSTR line_ = line;
    int ICP = client_dc.GetTextCharacterExtra();

    int TEC = (line_[pos]=='\t') ? tab_pxls
                     : (client_dc.GetTextExtent(&line_[pos],1).cx + ICP);
    for(int x_pos = 0; (x_pos+(TEC+1)/2 < pt.x) && (++pos !=
line.GetLength()); )
    {
     x_pos += TEC;
     TEC = (line_[pos]=='\t') ? ( tab_pxls-(x_pos%tab_pxls) )
                  : ( client_dc.GetTextExtent(&line_[pos], 1).cx + ICP );
    }
   }
  }
  client_dc.SelectObject(old_font);

  char_ind = LineIndex(line_ind) + pos;
 }
 return char_ind;

Quote:
}

// Retrieve line of text at line index
CString CTTEdit::GetTextAt_(int line_index)
{
 CString ret;

 long buf_size = LineLength(LineIndex(line_index))+1;
 LPTSTR line_buf = ret.GetBuffer(buf_size);
 int cc = GetLine((line_index==-1) ? (LineFromChar()) : (line_index),
line_buf, buf_size);
 ASSERT(cc>=0 && cc<buf_size);
 line_buf[cc] = '\0';

 ret.ReleaseBuffer();
 return ret;

Quote:
}

// Handler for WM_RBUTTONDOWN
// On right click outside selection, sets caret position to
// position clicked
void CTTEdit::OnRButtonDown(UINT nFlags, CPoint point)
{
 int beg_sel, end_sel, char_ind = CharFromPosEx(point);
 GetSel(beg_sel, end_sel);

 if(beg_sel==end_sel || char_ind<beg_sel || char_ind>end_sel)
  SetSel(char_ind, char_ind);

 CEdit::OnRButtonDown(nFlags, point);

Quote:
}

// Trap EM_SETTABSTOPS to remember tab stops
LRESULT CTTEdit::OnSetTabStops(WPARAM wParam, LPARAM lParam)
{
 if(wParam==0)
  tab_stops = DEF_TAB_DLU;
 else if(wParam==1)
  tab_stops = *reinterpret_cast<unsigned int*>( lParam );
 else
 {
  tab_stops = DEF_TAB_DLU; // not handled correctly yet
  ASSERT(FALSE);
 }
 return Default();
Quote:
}



Mon, 19 Sep 2005 14:55:33 GMT  
 Workaround for CEdit::CharFromPos (final!)
Again I noticed a bug in the workaround. When the text is scrolled
horizontally, the returned index is incorrect.
I'm working on a way to solve this.

Any help is still appreciated,
Tom T.


Quote:
> Hi groupies,

> I wrote a workaround for CEdit::CharFromPos (VC++6SP5), because it can
> only return charachter indices below 65536 (as a WORD). The workaround
> can return correct charachter indices for CEdit's with large amouts of
text
> >65535 charachters (as a UINT).
> The workaround is named CharFromPosEx. It also involves trapping
> EM_SETTABSTOPS to store the tab stops set, because CEdit doesn't have a
> method like GetTabStops.
> You can test the workaround's behaviour by adding the handler for
> WM_RBUTTONDOWN, which sets the caret on right clicking.

> Yt,
> Tom T.
> PS: Thanks Rajaraman for your hint.
> PS2: Any further remarks are still welcome of course ;)

> // HEADER
> class CTTEdit : public CEdit
> {
> public:
>  CTTEdit();
>  UINT CharFromPosEx(CPoint pt);
>  CString GetTextAt_(int line_index=-1);
> // ...
> protected:
>  //{{AFX_MSG(CTTEdit)
>  afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
>  //}}AFX_MSG
>  afx_msg LRESULT OnSetTabStops(WPARAM wParam, LPARAM lParam);
>  DECLARE_MESSAGE_MAP()
> // ...
> private:
>  unsigned int tab_stops;
> // ...
> };

> // IMPLEMENTATION
> #include <cmath> // for floor

> // convert dialog units 2 pixels (cx)
> #define DLU2PXL_X(dlu, factor) (((dlu)*(factor))/4)

> #define DEF_TAB_DLU 32

> BEGIN_MESSAGE_MAP(CTTEdit, CEdit)
>  //{{AFX_MSG_MAP(CTTEdit)
>  ON_WM_RBUTTONDOWN()
>  //}}AFX_MSG_MAP
>  ON_MESSAGE(EM_SETTABSTOPS, OnSetTabStops )
> END_MESSAGE_MAP()

> CTTEdit::CTTEdit()
> :  tab_stops(DEF_TAB_DLU)
> {/* ... */}

> // Returns charachter index at point
> UINT CTTEdit::CharFromPosEx(CPoint pt)
> {
>  CRect rect;
>  GetClientRect(&rect);

>  UINT char_ind = 0;
>  if(rect.PtInRect(pt))
>  {
>   CClientDC parent_dc(GetParent());
>   CFont* old_font = parent_dc.SelectObject( GetParent()->GetFont() );
>   CSize cs_alpha =

parent_dc.GetTextExtent(_T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv

- Show quoted text -

Quote:
> wxyz"), 52);
>   int av_cx_alpha = (cs_alpha.cx + 26) / 52;
>   int tab_pxls = DLU2PXL_X(tab_stops, av_cx_alpha);
>   parent_dc.SelectObject(old_font);

>   CClientDC client_dc(this);
>   old_font = client_dc.SelectObject( GetFont() );
>   int height_row = client_dc.GetTextExtent(" ").cy;
>   int line_count = GetLineCount();
>   int left_margin = static_cast<int>( LOWORD(GetMargins()) );

>   int line_ind = 0;
>   if(pt.y != 0)
>   {
>    line_ind = static_cast<int>( GetFirstVisibleLine() +
> floor((pt.y-1)/double(height_row)) );
>    if(line_ind > line_count-1)
>     line_ind = line_count-1;
>   }
>   int pos = 0;

>   if(pt.x >= left_margin)
>   {
>    CString line = GetTextAt_(line_ind);
>    if(!line.IsEmpty())
>    {
>     pt.x -= left_margin;
>     LPCTSTR line_ = line;
>     int ICP = client_dc.GetTextCharacterExtra();

>     int TEC = (line_[pos]=='\t') ? tab_pxls
>                      : (client_dc.GetTextExtent(&line_[pos],1).cx + ICP);
>     for(int x_pos = 0; (x_pos+(TEC+1)/2 < pt.x) && (++pos !=
> line.GetLength()); )
>     {
>      x_pos += TEC;
>      TEC = (line_[pos]=='\t') ? ( tab_pxls-(x_pos%tab_pxls) )
>                   : ( client_dc.GetTextExtent(&line_[pos], 1).cx + ICP );
>     }
>    }
>   }
>   client_dc.SelectObject(old_font);

>   char_ind = LineIndex(line_ind) + pos;
>  }
>  return char_ind;
> }

> // Retrieve line of text at line index
> CString CTTEdit::GetTextAt_(int line_index)
> {
>  CString ret;

>  long buf_size = LineLength(LineIndex(line_index))+1;
>  LPTSTR line_buf = ret.GetBuffer(buf_size);
>  int cc = GetLine((line_index==-1) ? (LineFromChar()) : (line_index),
> line_buf, buf_size);
>  ASSERT(cc>=0 && cc<buf_size);
>  line_buf[cc] = '\0';

>  ret.ReleaseBuffer();
>  return ret;
> }

> // Handler for WM_RBUTTONDOWN
> // On right click outside selection, sets caret position to
> // position clicked
> void CTTEdit::OnRButtonDown(UINT nFlags, CPoint point)
> {
>  int beg_sel, end_sel, char_ind = CharFromPosEx(point);
>  GetSel(beg_sel, end_sel);

>  if(beg_sel==end_sel || char_ind<beg_sel || char_ind>end_sel)
>   SetSel(char_ind, char_ind);

>  CEdit::OnRButtonDown(nFlags, point);
> }

> // Trap EM_SETTABSTOPS to remember tab stops
> LRESULT CTTEdit::OnSetTabStops(WPARAM wParam, LPARAM lParam)
> {
>  if(wParam==0)
>   tab_stops = DEF_TAB_DLU;
>  else if(wParam==1)
>   tab_stops = *reinterpret_cast<unsigned int*>( lParam );
>  else
>  {
>   tab_stops = DEF_TAB_DLU; // not handled correctly yet
>   ASSERT(FALSE);
>  }
>  return Default();
> }



Wed, 21 Sep 2005 13:24:11 GMT  
 Workaround for CEdit::CharFromPos (final!)


Wed, 18 Jun 1902 01:00:00 GMT  
 Workaround for CEdit::CharFromPos (final!)
This version is another bugfix that solves a problem regarding scrolling
horizontally. Hope that's the last bug :)

Tom T.

// HEADER
class CTTEdit : public CEdit
{
public:
 CTTEdit();
 UINT CharFromPosEx(CPoint pt);
 CString GetTextAt_(int line_index=-1);
// ...
protected:
 //{{AFX_MSG(CTTEdit)
 afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
 //}}AFX_MSG
 afx_msg LRESULT OnSetTabStops(WPARAM wParam, LPARAM lParam);
 DECLARE_MESSAGE_MAP()
// ...
private:
 unsigned int tab_stops;
// ...

Quote:
};

// IMPLEMENTATION
#include <cmath> // for floor

// convert dialog units 2 pixels (cx)
#define DLU2PXL_X(dlu, factor) (((dlu)*(factor))/4)

#define DEF_TAB_DLU 32

BEGIN_MESSAGE_MAP(CTTEdit, CEdit)
 //{{AFX_MSG_MAP(CTTEdit)
 ON_WM_RBUTTONDOWN()
 //}}AFX_MSG_MAP
 ON_MESSAGE(EM_SETTABSTOPS, OnSetTabStops )
END_MESSAGE_MAP()

CTTEdit::CTTEdit()
:  tab_stops(DEF_TAB_DLU)
{/* ... */}

// Returns charachter index at point
UINT CTTEdit::CharFromPosEx(CPoint pt)
{
 CRect rect;
 GetClientRect(&rect);

 UINT char_ind = 0;
 if(rect.PtInRect(pt))
 {
  CClientDC parent_dc(GetParent());
  CFont* old_font = parent_dc.SelectObject( GetParent()->GetFont() );
  CSize cs_alpha =
parent_dc.GetTextExtent(_T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv
wxyz"), 52);
  int av_cx_alpha = (cs_alpha.cx + 26) / 52;
  int tab_pxls = DLU2PXL_X(tab_stops, av_cx_alpha);
  parent_dc.SelectObject(old_font);

  CClientDC client_dc(this);
  old_font = client_dc.SelectObject( GetFont() );
  int height_row = client_dc.GetTextExtent(" ").cy;
  int line_count = GetLineCount();
  int left_margin = static_cast<int>( LOWORD(GetMargins()) );

  int line_ind = 0;
  if(pt.y != 0)
  {
   line_ind = static_cast<int>( GetFirstVisibleLine() +
floor((pt.y-1)/double(height_row)) );
   if(line_ind > line_count-1)
    line_ind = line_count-1;
  }
  int pos = 0;

  CString line = GetTextAt_(line_ind);
  if(!line.IsEmpty())
  {
   pt.x = (pt.x >= left_margin) ? (pt.x - left_margin) : 0;

   int min_pos, max_pos;
   GetScrollRange(SB_HORZ, &min_pos, &max_pos);
   pt.x += (GetScrollPos(SB_HORZ) - min_pos);

   LPCTSTR line_ = line;
   int ICP = client_dc.GetTextCharacterExtra();

   int TEC = (line_[pos]=='\t') ? tab_pxls
                    : (client_dc.GetTextExtent(&line_[pos],1).cx + ICP);
   for(int x_pos = 0; (x_pos+(TEC+1)/2 < pt.x) && (++pos !=
line.GetLength()); )
   {
    x_pos += TEC;
    TEC = (line_[pos]=='\t') ? ( tab_pxls-(x_pos%tab_pxls) )
                 : ( client_dc.GetTextExtent(&line_[pos], 1).cx + ICP );
   }
  }
  client_dc.SelectObject(old_font);

  char_ind = LineIndex(line_ind) + pos;
 }
 return char_ind;

Quote:
}

// Retrieve line of text at line index
CString CTTEdit::GetTextAt_(int line_index)
{
 CString ret;

 long buf_size = LineLength(LineIndex(line_index))+1;
 LPTSTR line_buf = ret.GetBuffer(buf_size);
 int cc = GetLine((line_index==-1) ? (LineFromChar()) : (line_index),
line_buf, buf_size);
 ASSERT(cc>=0 && cc<buf_size);
 line_buf[cc] = '\0';

 ret.ReleaseBuffer();
 return ret;

Quote:
}

// Handler for WM_RBUTTONDOWN
// On right click outside selection, sets caret position to
// position clicked
void CTTEdit::OnRButtonDown(UINT nFlags, CPoint point)
{
 int beg_sel, end_sel, char_ind = CharFromPosEx(point);
 GetSel(beg_sel, end_sel);

 if(beg_sel==end_sel || char_ind<beg_sel || char_ind>end_sel)
  SetSel(char_ind, char_ind);

 CEdit::OnRButtonDown(nFlags, point);

Quote:
}

// Trap EM_SETTABSTOPS to remember tab stops
LRESULT CTTEdit::OnSetTabStops(WPARAM wParam, LPARAM lParam)
{
 if(wParam==0)
  tab_stops = DEF_TAB_DLU;
 else if(wParam==1)
  tab_stops = *reinterpret_cast<unsigned int*>( lParam );
 else
 {
  tab_stops = DEF_TAB_DLU; // not handled correctly yet
  ASSERT(FALSE);
 }
 return Default();
Quote:
}



Wed, 21 Sep 2005 14:51:56 GMT  
 
 [ 4 post ] 

 Relevant Pages 

1. CEdit::CharFromPos workaround: almost there ... one more problem

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 the Final Episode)

6. CRichEditCtrl & CharFromPos

7. CharFromPos and GetCaretPos usage

8. CEdit box ** CEdit box ** CEdit box

9. C# compiler bug--no word of a workaround anyplace...workaround found!

10. Covariant return type in managed class workaround?

11. A workaround?

12. Workaround for constant re-builds

 

 
Powered by phpBB® Forum Software