[2 days left] What’s wrong with your cloud strategy? Learn why multicloud solutions matter with Nimble Storage.Register Now

x
?
Solved

CRichEditView text formatting

Posted on 1998-04-09
14
Medium Priority
?
638 Views
Last Modified: 2013-11-20
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.
0
Comment
Question by:openGL
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
  • 4
14 Comments
 
LVL 11

Expert Comment

by:mikeblas
ID: 1317576
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
 

Author Comment

by:openGL
ID: 1317577
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
 
LVL 11

Expert Comment

by:mikeblas
ID: 1317578
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:openGL
ID: 1317579
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
 
LVL 10

Accepted Solution

by:
RONSLOW earned 400 total points
ID: 1317580
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
 

Author Comment

by:openGL
ID: 1317581
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
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1317582
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
 
LVL 11

Expert Comment

by:mikeblas
ID: 1317583
CHARFORMAT is a struct, not a class. Worrying about a memset() call on it is a waste of breath.

.B ekiM

0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1317584
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
 
LVL 11

Expert Comment

by:mikeblas
ID: 1317585
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
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1317586
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
 

Author Comment

by:openGL
ID: 1317587
Hey people! The question was answered! The app works great! My god, stop arguing for pete's sake!! :)
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1317588
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
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1317589
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

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

If you use Adobe Reader X it is possible you can't open OLE PDF documents in the standard. The reason is the 'save box mode' in adobe reader X. Many people think the protected Mode of adobe reader x is only to stop the write access. But this fe…
In this post we will learn different types of Android Layout and some basics of an Android App.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…
Suggested Courses

649 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question