CRichEditView text formatting

ok, I'm trying to change the format of the text in a CRichEditView...nothing fancy, I'd just like to set the tabs to a width of 4 characters and to change the font to a user specified font, which is represented as the CFont variable "currfont."  Here's what I have so far:

CRichEditCtrl &ctrl = GetRichEditCtrl();

CHARFORMAT cf;
memset(&cf, 0, sizeof(cf));
LOGFONT lf;
memset(&lf, 0, sizeof(lf));

currfont->GetLogFont(&lf);

cf.cbSize = sizeof(cf);
cf.dwMask = CFM_FACE | CFM_ITALIC | CFM_SIZE | CFM_STRIKEOUT | CFM_UNDERLINE | CFM_COLOR | CFM_BOLD;
if (lf.lfItalic)
      cf.dwEffects |= CFE_ITALIC;
if (lf.lfStrikeOut)
      cf.dwEffects |= CFE_STRIKEOUT;
if (lf.lfUnderline)
      cf.dwEffects |= CFE_UNDERLINE;
cf.dwEffects |= CFE_AUTOCOLOR;
cf.yHeight = lf.lfHeight;
cf.bCharSet = lf.lfCharSet;
cf.bPitchAndFamily = lf.lfPitchAndFamily;
strcpy(cf.szFaceName,lf.lfFaceName);
ctrl.SetDefaultCharFormat(cf);

PARAFORMAT pf;
int i;
pf.cbSize = sizeof(pf);
pf.dwMask = PFM_TABSTOPS;
pf.cTabCount = 20;
for (i = 0; i < 20; i++)
     pf.rgxTabs[i] = i * 4;
ctrl.SetParaFormat(pf);

Obviously, it doesn't bold the text, even with "bold" selected (although it would be nice if it would), but most perplexing is that it won't change the text size and the tabs are completely screwed up.  Any idea how to change this so it works?

                         -W.W.
P.S. The text should not be able to change color...i.e. it should be plain black.
openGLAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

mikeblasCommented:
You're not setting the tab stops correctly--you don't understand how tab stops in the control work.

The control lets you specify tab stop positions in twips, not in character columns. The control may display proportional width fonts, so it's impossible to know that a tab stop happens every nth character unless you also know that you're using a monospace font.

If you're using a monospace font and want to have the tabstops at every 4th character, you'll need to do some conversions to get the right values for the tabstops array. Your code above already gets the LOGFONT for your font, and that has an lfWidth member which shows the average width of a character in the font in pixels on the device where the font's selected.

Assuming your font is selected into a display device, you can get the ratio between horizontal pixels and inches by asking the GetDeviceCaps() function for LOGPIXELSX.  So, that might look like:

   HDC hdc = ::GetDC();
   int nPixels = ::GetDeviceCaps(hdc, LOGPIXELSX);

Then, since you now know that there are nPixels for each inch on your display, and you know that there are 1440 twips per inch, you can get the number of twips for the width of the characters in your font with this expression:

   int nWidth = (logfont.lfWidth * nPixels) / 1440;

Then, you'd use four-times that value in your loop:

   PARAFORMAT pf;
   int i;
   pf.cbSize = sizeof(pf);
   pf.dwMask = PFM_TABSTOPS;
   pf.cTabCount = 20;
   for (i = 0; i < 20; i++)
        pf.rgxTabs[i] = nWidth * i * 4;
   ctrl.SetParaFormat(pf);

and that's that.

.B ekiM  



.B ekiM

0
openGLAuthor Commented:
OK, couple of problems here, the first and foremost being that the above code doesn't work, mostly because the lfWidth of the LOGFONT struct is always 0.  The second problem is that you didn't even address how to change the size of the font...lfHeight is always a negative number which has little to do with the font size...help me out here.

        -W.W.
0
mikeblasCommented:
lfWidth will be zero if the font hasn't been selected into a device context.  You've got to select it someplace before it has metrics.

.B ekiM
0
Build an E-Commerce Site with Angular 5

Learn how to build an E-Commerce site with Angular 5, a JavaScript framework used by developers to build web, desktop, and mobile applications.

openGLAuthor Commented:
what are you talking about? a logical font can have an average width without being selected into a dc, and anyway lets say I have the following:
      dc.SelectObject(currfont);
      CRichEditCtrl &ctrl = GetRichEditCtrl();
      CHARFORMAT cf;
      memset(&cf, 0, sizeof(cf));
      LOGFONT lf;
      memset(&lf, 0, sizeof(lf));
      SetFont(currfont);
      currfont->GetLogFont(&lf);
      PARAFORMAT pf;
      int i;
      CClientDC dc(this);
      int Pixels = dc.GetDeviceCaps(LOGPIXELSX);
      int Width = (lf.lfWidth * Pixels) / 1440;
      pf.cbSize = sizeof(pf);
      pf.dwMask = PFM_TABSTOPS;
      pf.cTabCount = 20;
      for (i = 0; i < 20; i++)
            pf.rgxTabs[i] = Width * i * 4;
      ctrl.SetParaFormat(pf);
Wouldn't the call to SetFont select the font into the dc anyway? In any case, why is GetLogFont() not returning the correct values for the font?
0
RONSLOWCommented:
Font height can be negative - it means you are specifying char height instead of cell height.  it can bne zero as well, which means use a sensible default.

Font width can be zero - it means to calculate the width from the height based on aspect ratios etc

So GetLogFont may well be returning the correct values anyway.

SetFont won't select it into the DC - it just sets what the default font will be for that window when a dc is created.  It is SelectObject that selects a font into a DC

To get the average char width, why not just use GetTextMetric after selecting the font into the DC with SelectObject ?

PS: in general code like
  CHARFORMAT cf;
  memset(&cf, 0, sizeof(cf));
is not nice (and sometimes unsafe) in C++ (when classes may have vtables and references etc) .. maybe you could just try
  CHARFORMAT cf = {0};

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
openGLAuthor Commented:
Seeing as how CHARFORMAT is a struct and not a class, I don't see anything wrong with the above code.  In any case, Using GetTextMetric returns a number from 9-11 as the average width...now what? The equation mentioned earlier (e.g. tabwidth = LOGPIXELSX * fontwidth / 1440) doesn't work since the resulting answer always comes out to be less than one.
0
RONSLOWCommented:
You've gotten it backwards...

GetTextMetrics returns the height in logical units.
GetDeviceCaps(LOGPIXELSX) return the logical units per inch
1440 is the number of tips per inch (a twip is 1/1440th")

You need to convert the logical units to twips .

So, you need to divide logical units by logpixelsx to give number of inches and multiply by 1440 to give twips ie.

logpixelsx = pDC->GetDeviceCaps(LOGPIXELSX);
twipwidth = ::MulDiv(tmAveCharWidth,1440,LOGPIXELSX);

BTW: if you ever do need to do calcs like that, use MulDiv rather than doing a multiply and divide to avoid integer trunctation errors.

0
mikeblasCommented:
CHARFORMAT is a struct, not a class. Worrying about a memset() call on it is a waste of breath.

.B ekiM

0
RONSLOWCommented:
I was saying in general using memset on a class or struct (they are equivalent anyway apart from default member access) is bad news and should be avoided.

If it is your own class/struct, then you should provide a constructor that initializes the members.

If it is someone elses, then you shouldn't assume that it is safe to do a memset - in case the other person changes the implementation.

Yes, in this PARTICULAR case it happens to be OK, but it is just bad programming practice.

I don't believe good programming practice is a "waste of breath".

0
mikeblasCommented:
It's not bad programming practice as there's nothing wrong with calling memset() on a struct. struct's aren't equivalent to classes, especially the way you're measuring them here: a struct can't have virtual functions.

Even when a class does have virtual functions, using memset() on a pointer to the class for sizeof(theclass) sets the member data, not the vtable.

Good programming practice isn't a waste of breath, but dubious advice certainly is.

.B ekiM

0
RONSLOWCommented:
Wrong on several counts there, Mike.

* structs ARE equivalent to classes - the ONLY difference is that the members are public rather than private by default.

* A struct CAN have virtual functions (and constructors etc) and can dervie from other structs etc

* sizeof DOES include the vtable pointer.

* Doing a memset on a struct with virtual functions kills the vtable pointer

This PARTICULAR struct is a simple one with no constructors or virtual functions etc, so the memset is safe.  BUT in GENERAL it is STILL not good practice.

Please get your facts straight, Mike, before you criticise my advice as being 'dubious', especially when yours is just plain wrong !!


PS: Here is some code that demonstrates this

#include <stdio.h>
#include <stdlib.h>
struct S {
  int x;
  S() : x(1) {}
  virtual int f() { return x; }
};
struct T {
  int x;
};
int main (void) {
  S s;  T t;  S* ps = &s;
  printf ("%u\n",sizeof(s));
  printf ("%u\n",sizeof(t));
  int i; unsigned char* p;
  p = reinterpret_cast<unsigned char*>(&s);
  printf("%p %p :",p,&s);
  for (i = 0; i < sizeof(s); i++) {
    int byte = *p++;
    printf (" %2x",byte);
  }
  printf("\n");
  printf("%p %p :",p,&t);
  p = reinterpret_cast<unsigned char*>(&t);
  for (i = 0; i < sizeof(t); i++) {
    int byte = *p++;
    printf (" %2x",byte);
  }
  printf("\n");
  printf ("calling ps->f() gives %d\n",ps->f());
  memset(&s,0,sizeof(s));
  p = reinterpret_cast<unsigned char*>(&s);
  printf("%p %p :",p,&s);
  for (i = 0; i < sizeof(s); i++) {
    int byte = *p++;
    printf (" %2x",byte);
  }
  printf("\n");
  printf("%p %p :",p,&t);
  memset(&t,0,sizeof(t));
  p = reinterpret_cast<unsigned char*>(&t);
  for (i = 0; i < sizeof(t); i++) {
    int byte = *p++;
    printf (" %2x",byte);
  }
  printf("\n");
  printf ("calling ps->f() gives %d\n",ps->f());
  return 0;
}

Here is the (anotated) output from it

8    // sizeof S includes the vtable
4    // sizeof T does not
0064FDEC 0064FDEC : 54 f0 40  0  1  0  0  0    // see the vtable and initial value
0064FDF4 0064FDE8 : 3f  1  0  0    // rubbish because no constructor
calling ps->f() gives 1    // virtual function call works
0064FDEC 0064FDEC :  0  0  0  0  0  0  0  0    // no vtable any more
0064FDF4 0064FDE8 :  0  0  0  0    // zeroed out
and then it crashes due to the vtable pointer being destroyed.

0
openGLAuthor Commented:
Hey people! The question was answered! The app works great! My god, stop arguing for pete's sake!! :)
0
RONSLOWCommented:
I am smply answering the invalid criticisms Mike made of my advice to (in general) not use memset of classes and structs

Unless you are sure that the class/struct does NOT have virtual functions or constructors, and if it contains or derives from other classes/structs that these also satisfy this requirement.  (ie. it should be an "aggregate").

In such a case then it is safe to use a memset, BUT it is safer to use an initializer when defining the struct variable.  An initializer will not compile if the struct is NOT an aggregate (protecting yourself from errors), whereas memset will clobber anything it is given).

I know that the answer has been given and these points are not directly relevant to the question.  However, this thread will remain in the PAQ and I would not like to see someone read this and go away believing that memset's are always safe with structs and cannot clobber vtable pointers in classes when that is simply NOT the case.

.O regoR

:-)

0
RONSLOWCommented:
Mike, perhaps you were getting confused between structs and unions?

Unions cannot be derived from, nor can they dervie from a class or struct).  Unions cannot have virtual functions (although they can still have member functions including constructors and destructors - unless anonymous).

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.