Board index » cppbuilder » Possible to just get visible text of a TMemo?

Possible to just get visible text of a TMemo?


2005-05-26 01:30:58 PM
cppbuilder87
I can't seem to find a way to get just the *visible* text of a TMemo.
I'm working on a project that sends messages to a messaging program that
can be no bigger than the size of a memo (multiline textbox) and this
box resides in the window I find with FindWindow and use SendMessage to
put the text in.
This textbox uses a proportional font (Arial, 8pt) and cannot exceed the
*visible* size, instead of a character limit. If I SendMessage (using
WM_SETTEXT) it will even insert text that's longer than the visible
portion of the textbox, and if I try to send I get an error.
Therefore I need to be able to test to make sure it will fit before I
try to insert it to the external program.
Thank for any help. Google groups and www sadly have been no help with
this.
 
 

Re:Possible to just get visible text of a TMemo?

Segal wrote:
Quote
I can't seem to find a way to get just the *visible* text of a TMemo.

I'm working on a project that sends messages to a messaging program
that can be no bigger than the size of a memo (multiline textbox) and
this box resides in the window I find with FindWindow and use
SendMessage to put the text in.

This textbox uses a proportional font (Arial, 8pt) and cannot exceed
the *visible* size, instead of a character limit. If I SendMessage
(using WM_SETTEXT) it will even insert text that's longer than the
visible portion of the textbox, and if I try to send I get an error.

Therefore I need to be able to test to make sure it will fit before I
try to insert it to the external program.

Thank for any help. Google groups and www sadly have been no help with
this.
Sorry if I was not clear. THe external textbox (or memo) is wordwrapped,
and my goal is to set up a local memo (with visibility set to false so
its just a work horse in the background) the same size and font, and
wordwrapped, as the target external memo so I can see if the text I want
to send will fit.
My only problem is I'm not sure how to check if the text I put in my
local "test" memo is fully visible and not longer than the visible area
(meaning if it extends past the visible area, I need to get just the
visible text so I can split the message in 2 or more parts as
necissary.)
Thanks again.
 

Re:Possible to just get visible text of a TMemo?

"Segal" < XXXX@XXXXX.COM >wrote:
Quote

[...] so I can split the message [...]
Allocate a TControlCanvas and assign the TMemo to the
TControlCanvas's Control property. Then use the TCanvas's
TextWidth method to determine how many pixels the string
requires.
~ JD
 

{smallsort}

Re:Possible to just get visible text of a TMemo?

JD wrote:
Quote
"Segal" < XXXX@XXXXX.COM >wrote:
>
>[...] so I can split the message [...]

Allocate a TControlCanvas and assign the TMemo to the
TControlCanvas's Control property. Then use the TCanvas's
TextWidth method to determine how many pixels the string
requires.

~ JD
Thank you. This still doesn't allow me ot get the text of just the
visible area of the memo.
Perhaps theres a way to limit use the TextWidth to limit how many chars
can be put in:
For example, on my word wrapped memo
+-----------------------+
| la la la la la bling |
| bling blang ting tang |
| a walla walla bing ba |
------------------------+
At first it returns
579.
If I go to the very end (which is in fact a trailing new line that is
the start of the 4th line which is out of view) and hit backspace, I get
573.
But what would be a good way to effectively substring a 573 pixel
portion if TextWidth of the ControlCanvas exceeds 573?
 

Re:Possible to just get visible text of a TMemo?

"Segal" < XXXX@XXXXX.COM >wrote in message
Quote
My only problem is I'm not sure how to check if the text I put in my
local "test" memo is fully visible and not longer than the visible
area (meaning if it extends past the visible area, I need to get
just the visible text so I can split the message in 2 or more parts
as necissary.)
Why don't you try something like this:
// Untested.
void __fastcall PosReverse( AnsiString Text, AnsiString SearchText,
int From = 0 );
AnsiString __fastcall GetVisibleText( TMemo *Memo1 );
AnsiString __fastcall GetVisibleText( TMemo *Memo1 )
{
int i, l, t, p;
int x = 0;
int y = 0;
int w = Memo1->ClientWidth;
int h = Memo1->ClientHeight;
AnsiString Text = Memo1->Text;
TControlCanvas *Canvas = new TControlCanvas;
l = Text.Length();
// Loop through every character, wrapping the text
// until the height of the text exceed the memo's height.
for( i=1;i<=l;i++ )
{
// Find out the width of the next char.
t = Canvas->TextWidth( Text.SubString( i, 1 ) );
// If the width of the char plus the width of the chars
// before it exceed the width of the memo, then
// wrap the line.
if( ( x + t )>= w )
{
// Search for the last space.
p = PosReverse( Text, " ", i );
if( p < 1 )
{
// Didn't find a space, so just force wrap the line.
x = t;
y += Canvas->TextHeight( "My" );
}
else
{
// Did find a space, so wrap the line at that point.
x = 0;
i = p;
y += Canvas->TextHeight( "My" );
}
}
if( y>h )
{
// The text on the next loop would exceed the memo's
// height, so we exit the loop now.
break;
}
}
delete Canvas;
return Text.SubString( 1, i );
}
void __fastcall PosReverse( AnsiString Text, AnsiString SearchText,
int From )
{
if( From == 0 )
From = Text.Length();
for( ;From>0;From-- )
{
if( Text.SubString( From, SearchText.Length() ) ==
SearchText )
break;
}
return From;
}
 

Re:Possible to just get visible text of a TMemo?

There is always the KISS principle you can take advantage of (keeping it
simple s.....)
Memo1->Lines->Count;
Record the number of lines that you are allowed to visually see in the
memo and then make sure what ever you want to send in your test memo
isn't displaying more than that number of lines..
Jonathan Benedicto wrote:
Quote
"Segal" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>My only problem is I'm not sure how to check if the text I put in my
>local "test" memo is fully visible and not longer than the visible
>area (meaning if it extends past the visible area, I need to get
>just the visible text so I can split the message in 2 or more parts
>as necissary.)


Why don't you try something like this:

// Untested.

void __fastcall PosReverse( AnsiString Text, AnsiString SearchText,
int From = 0 );
AnsiString __fastcall GetVisibleText( TMemo *Memo1 );

AnsiString __fastcall GetVisibleText( TMemo *Memo1 )
{
int i, l, t, p;
int x = 0;
int y = 0;
int w = Memo1->ClientWidth;
int h = Memo1->ClientHeight;
AnsiString Text = Memo1->Text;
TControlCanvas *Canvas = new TControlCanvas;

l = Text.Length();

// Loop through every character, wrapping the text
// until the height of the text exceed the memo's height.
for( i=1;i<=l;i++ )
{
// Find out the width of the next char.
t = Canvas->TextWidth( Text.SubString( i, 1 ) );

// If the width of the char plus the width of the chars
// before it exceed the width of the memo, then
// wrap the line.
if( ( x + t )>= w )
{
// Search for the last space.
p = PosReverse( Text, " ", i );
if( p < 1 )
{
// Didn't find a space, so just force wrap the line.
x = t;
y += Canvas->TextHeight( "My" );
}
else
{
// Did find a space, so wrap the line at that point.
x = 0;
i = p;
y += Canvas->TextHeight( "My" );
}
}
if( y>h )
{
// The text on the next loop would exceed the memo's
// height, so we exit the loop now.
break;
}
}

delete Canvas;

return Text.SubString( 1, i );
}

void __fastcall PosReverse( AnsiString Text, AnsiString SearchText,
int From )
{
if( From == 0 )
From = Text.Length();

for( ;From>0;From-- )
{
if( Text.SubString( From, SearchText.Length() ) ==
SearchText )
break;
}

return From;
}


 

Re:Possible to just get visible text of a TMemo?

"Daniel Mayo" < XXXX@XXXXX.COM >wrote in message
Quote
There is always the KISS principle you can take advantage of
(keeping it simple s.....)

Memo1->Lines->Count;

Record the number of lines that you are allowed to visually see in
the memo and then make sure what ever you want to send in your test
memo isn't displaying more than that number of lines..
I didn't think of that. Yes, I am often very s..... :-(
Jonathan
 

Re:Possible to just get visible text of a TMemo?

"Jonathan Benedicto" < XXXX@XXXXX.COM >wrote:
Quote

// Untested.
You failed to assign the TControlCanvas's Control property
so I looked no further.
~ JD
 

Re:Possible to just get visible text of a TMemo?

"Segal" < XXXX@XXXXX.COM >wrote:
Quote

But what would be a good way to effectively substring a 573 pixel
portion if TextWidth of the ControlCanvas exceeds 573?
The easiest way is to use a copy of the string. If the
TextWidth exceeds your limit, set it's last character to NULL
and test it's width again. Once you find the last character
that falls within the limit, all you have to do is continue
backing up until you find a word break (space). Then you can
use the sub-string length to know where to resume processing
with the original string.
To improve performance, initially backup multiple characters
to limit your calls to TextWidth.
~ JD
 

Re:Possible to just get visible text of a TMemo?

"JD" < XXXX@XXXXX.COM >wrote in message
Quote
You failed to assign the TControlCanvas's Control property
so I looked no further.
// Untested.
void __fastcall PosReverse( AnsiString Text, AnsiString SearchText,
int From = 0 );
AnsiString __fastcall GetVisibleText( TMemo *Memo1 );
AnsiString __fastcall GetVisibleText( TMemo *Memo1 )
{
int i, l, t, p;
int x = 0;
int y = 0;
int w = Memo1->ClientWidth;
int h = Memo1->ClientHeight;
AnsiString Text = Memo1->Text;
TControlCanvas *Canvas = new TControlCanvas;
Canvas->Control = Memo1;
l = Text.Length();
// Loop through every character, wrapping the text
// until the height of the text exceed the memo's height.
for( i=1;i<=l;i++ )
{
// Find out the width of the next char.
t = Canvas->TextWidth( Text.SubString( i, 1 ) );
// If the width of the char plus the width of the chars
// before it exceed the width of the memo, then
// wrap the line.
if( ( x + t )>= w )
{
// Search for the last space.
p = PosReverse( Text, " ", i );
if( p < 1 )
{
// Didn't find a space, so just force wrap the line.
x = t;
y += Canvas->TextHeight( "My" );
}
else
{
// Did find a space, so wrap the line at that point.
x = 0;
i = p;
y += Canvas->TextHeight( "My" );
}
}
if( y>h )
{
// The text on the next loop would exceed the memo's
// height, so we exit the loop now.
break;
}
}
delete Canvas;
return Text.SubString( 1, i );
}
void __fastcall PosReverse( AnsiString Text, AnsiString SearchText,
int From )
{
if( From == 0 )
From = Text.Length();
for( ;From>0;From-- )
{
if( Text.SubString( From, SearchText.Length() ) ==
SearchText )
break;
}
return From;
}
 

Re:Possible to just get visible text of a TMemo?

I sent that by accident. Please look at it a little further.
Jonathan
 

Re:Possible to just get visible text of a TMemo?

Daniel Mayo < XXXX@XXXXX.COM >wrote:
Quote

Please trim your posts.
Quote
There is always the KISS principle you can take advantage of
(keeping it simple s.....)

Memo1->Lines->Count;

Record the number of lines that you are allowed to visually
see in the memo and then make sure what ever you want to
send in your test memo isn't displaying more than that
number of lines.
That idea is sound but Memo::Lines::Count has nothing to do
with reaching that objective. To do what you suggested, you
would need to get the maximum height in pixels for any given
line and divide that into the height of the Memo.
~ JD
 

Re:Possible to just get visible text of a TMemo?

Quote
That idea is sound but Memo::Lines::Count has nothing to do
with reaching that objective. To do what you suggested, you
would need to get the maximum height in pixels for any given
line and divide that into the height of the Memo.

~ JD
JD,
I think (but not definitely sure) that height of a line in
TMemo is const, and that it depends on a font name & height,
and that it does not depend of a contents of text. In other
words I think that the height of one line in Memo is the same
if the string is "Wj" or just ".".
Once I needed similar thing and noticed that just multiplying
(in my case for some Arial font) with 14 gave me every time
the exact TMemo::Heigth.
But, calculating the width is the different story, and as
you said TControlCanvas is the solution (for non-word wrapped
text).
P.S.
Once, I needed the reverse thing - to expand the width of
TRichEdit to the minimum width to hold the whole row of text.
I couldn't use TControlCanvas because RTF can have various
formating (colors, boldness, etc), and then Remy has found
the solution that bothered me a long time. If someone is
interested, heres the solution:
tinyurl.com/d6h9b
Best regards,
Vladimir Stefanovic
 

Re:Possible to just get visible text of a TMemo?

"Jonathan Benedicto" < XXXX@XXXXX.COM >wrote:
Quote

[...] Please look at it a little further.
In all honesty, I didn't appreciate the logic and found it
difficult to trace through. This is what I wipped-up:
AnsiString Result = SendThisString( TheString );
while( Result.Length()>0 )
{
Result = SendThisString( Result );
}
AnsiString __fastcall TForm1::SendThisString( AnsiString TheString )
{
// scroll the memo
std::auto_ptr<TControlCanvas>pCanvas ( new TControlCanvas );
pCanvas->Control = Memo1;
AnsiString s = TheString;
int MaxLines = Memo1->ClientHeight / pCanvas->TextHeight( "Wg" );
TRect R = Memo1->ClientRect;
if( ::DrawText(pCanvas->Handle, s.c_str(), -1, &R, DT_CALCRECT)>Memo1->ClientHeight )
{
AnsiString NewString = "", temp = TheString;
for( int x = 0; x < MaxLines; ++x )
{
while( pCanvas->TextWidth(s)>Memo1->ClientWidth ) s.Delete( s.Length(), 1 );
if( temp[ s.Length() + 1 ] != ' ' )
{
while( s[ s.Length() ] != ' ' ) s.Delete( s.Length(), 1 );
}
if( x == MaxLines - 1 ) s.Delete( s.Length(), 1 );
NewString += s;
temp.Delete( 1, s.Length() );
s = temp;
}
s = NewString;
}
// send this string (s) to the memo
TheString.Delete( 1, s.Length() );
return TheString;
}
~ JD
 

Re:Possible to just get visible text of a TMemo?

"JD" < XXXX@XXXXX.COM >wrote in message
Quote
In all honesty, I didn't appreciate the logic and found it
difficult to trace through. This is what I wipped-up:
One could also just use DrawText in a loop removing words until the
height returned by DrawText does not exceed the memo height.
Something like this:
// Untested
AnsiString __fastcall TForm1::GetVisibleMemoText( TMemo *Memo1,
AnsiString Text )
{
int i;
TRect R = Memo1->ClientRect;
boost::shared_ptr<TControlCanvas>CanvasPtr( new TControlCanvas );
CanvasPtr->Control = Memo1;
while( ::DrawText(pCanvas->Handle, Text.c_str(), Text.Length(),
&R, DT_CALCRECT | DT_WORDBREAK)>Memo1->ClientHeight )
{
for( i=Text.Length();i>0;i--)
{
if( Text[i] == '\n' || Text[i] == ' ' )
break;
}
if( i == 0)
break;
Text.Delete( i, Text.Length() - i );
R = Memo1->ClientRect;
};
return Text;
}