Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

RTF Highlight command 1

Status
Not open for further replies.

Koen Piller

Programmer
Jun 30, 2005
841
NL
Hi,

With RTF you can search and highlight for words in your RTF-text. However the highlight function is not as the default highlight in Word or Excel: it changes the forecolor to a given color and not the backcolor. Anybody to give me the code to change the highlight to true highlight?

I am using these procedures now:
Code:
PROCEDURE SEARCHIT
* cmd Search

Local ;
	lcFindExpression As String, ;
	liExpressionFound As Integer


If Empty(Thisform.Text1.Value)
	Return
Endif

With Thisform
	m.lcFindExpression = Alltrim(.Text1.Value)

	m.liExpressionFound = .oleDetails.Find(m.lcFindExpression ,0, -1)
	If m.liExpressionFound >-1
		.nStart = m.liExpressionFound + 1
	Endif
Endwith
ENDPROC

PROCEDURE SEARCHALL
Local ;
	lcExpressionSought As String, ;
	liFound As Integer

If Empty(Thisform.Text1.Value)
	Return
Endif

m.lcExpressionSought = Alltrim(Thisform.Text1.Value)

With Thisform
	.nStart = 0
	m.liFound = 0
	Do While m.liFound >=0
		m.liFound = .oleDetails.Find(m.lcExpressionSought ,.nStart, -1)
		If m.liFound >-1
			.nStart = m.liFound + 1
* highlight it
			.oleDetails.SelColor = Rgb(255,0,0)  &&red
			.oleDetails.SelBold = .T.
		Else
			Exit
		Endif
	Enddo
Endwith
ENDPROC

Anyone who has the code to change the highlight to as it should be?

Stay healthy,
Koen
 
The only way to highlight text in RTF is to use .SelStart and .SelLength. The challenge is knowing what those values are.

So Koen, if you find a word in a 500 word RTF doc, how are you going to determine the .SelStart and .SelLength values of the word? You can determine .SelLength using LEN() of the searched word, but determining .SelStart is the tricky part.
 
VernPace, Nigel

I must conclude from your answers, I am asking for the impossible. That's a pity.
Stay healthy,
Koen

 
Koen,

No, you are not asking for the impossible as I have shown you in this thread (via code and reference links) how to do it. It will, however, require learning and digging on your part to acheive the result. I cannot legally provide ALL source code for certain solutions. The question is how badly do you want it and to what lengths are you willing to go to acheive it. This is about extending the usefullness of VFP as a programming lanquage and elevating our level of expertise.
 
I use the following code to perform a search for text in the MS Richtext active-x. In my richtext form, I have two buttons: 'Find' and 'Find Again'. The 'Find' button calls a form for the user to enter the search string and select 'Whole Word Only' and 'Match Case'. This child form calls the below method in the richtext form via RAISEEVENT() command. This will then trigger the search method of the richtext activex which also highlights the found text.

2021-02-02_15-12-28_hktvgo.png


Code:
PROCEDURE OnFormFindText
LPARAMETERS tcSrchText, tlWholeWord, tlMatchCase
WITH thisform
	.FindText  = tcSrchText
	.WholeWord = tlWholeWord
	.MatchCase = tlMatchCase
	DO CASE
		CASE !.WholeWord .AND. !.MatchCase
			.FoundIndex = .pgframe._page1.oleRichText.Find(.FindText, 0, , 0)

		CASE .WholeWord .AND. !.MatchCase
			.FoundIndex = .pgframe._page1.oleRichText.Find(.FindText, 0, , 2)

		CASE !.WholeWord .AND. .MatchCase
			.FoundIndex = .pgframe._page1.oleRichText.Find(.FindText, 0, , 4)

		CASE .WholeWord .AND. .MatchCase
			.FoundIndex = .pgframe._page1.oleRichText.Find(.FindText, 0, , 6)
	ENDCASE
	IF .FoundIndex >= 0
		.pgframe._page1.cmdFindAgain.Enabled = True
	ELSE
		DisplayMsg("Not found")
		.pgframe._page1.cmdFindAgain.Enabled = False
	ENDIF
	.pgframe._page1.oleRichText.SetFocus()
ENDWITH

The following are properties created on the richtext form:

Code:
FindText
WholeWord
MatchCase
FoundIndex

The 'Find Again' button has in its Click() event the following code:

Code:
PROCEDURE Click
WITH thisform
	DO CASE
		CASE !.WholeWord .AND. !.MatchCase
			.FoundIndex = .pgframe._page1.oleRichText.Find(.FindText, .FoundIndex+1, , 0)

		CASE .WholeWord .AND. !.MatchCase
			.FoundIndex = .pgframe._page1.oleRichText.Find(.FindText, .FoundIndex+1, , 2)

		CASE !.WholeWord .AND. .MatchCase
			.FoundIndex = .pgframe._page1.oleRichText.Find(.FindText, .FoundIndex+1, , 4)

		CASE .WholeWord .AND. .MatchCase
			.FoundIndex = .pgframe._page1.oleRichText.Find(.FindText, .FoundIndex+1, , 6)
	ENDCASE
	IF .FoundIndex < 0
		DisplayMsg("Not found")
		this.Enabled = False
		.FindText = ""
	ENDIF
	.pgframe._page1.oleRichText.SetFocus()
ENDWITH

The first call to the Find method returns the index of the found occurrence. I increment it in the 'Find Again' so that it will find the next occurrence. You can then use the API to change the selected text highlight when an occurrence is found.

Code:
LOCAL lcCharFormat
WITH this.Parent
	IF .oleRtfText.SelLength > 0
		.clsCharFormat.crBackColor = .shpHighLight.FillColor
		.clsCharFormat.dwMask      = CFM_BACKCOLOR
*		.clsCharFormat.dwEffects   = CFE_PROTECTED
*		.clsCharFormat.dwMask      = BITOR(CFM_PROTECTED, CFM_BACKCOLOR)
		lcCharFormat = .clsCharFormat.GetString()
		IF !EMPTY(lcCharFormat)
			IF apiSendMessage(.oleRtfText.hWnd, EM_SETCHARFORMAT, BITOR(SCF_SELECTION, SCF_WORD), @lcCharFormat) != 0
				IF PEMSTATUS(.Parent, "IsDirty", 5)
					.Parent.IsDirty = True
				ENDIF
			ENDIF
		ENDIF
	ENDIF
ENDWITH

clsCharFormat is based on the STRUCT.VCX by Christof Lange (see
Hope this helps.

Greg
 
Greg,
That seems to be the solution. I will report back asap after study of Christoff's article and your code.
Can you give me the values of the constants you used?
CFM_BACKCOLOR
CFE_PROTECTED
EM_SETCHARFORMAT
SCF_SELECTION
SCF_WORD
Thank you so much
Stay healthy
Koen
 
Here is the class with the ones that I created for the richtext:


Here are the Richtext constants

Code:
*-*
*-*	MS RichText constants
*-*
#DEFINE WM_USER	                0x400
#DEFINE EM_SETZOOM             (WM_USER + 225)
#DEFINE EM_GETCHARFORMAT       (WM_USER + 58)
#DEFINE EM_SETCHARFORMAT       (WM_USER + 68)
#DEFINE EM_GETPARAFORMAT       (WM_USER + 61)
#DEFINE EM_SETPARAFORMAT       (WM_USER + 71)
#DEFINE EM_REDO                (WM_USER + 84)
#DEFINE EM_CANREDO             (WM_USER + 85)
#DEFINE EM_UNDO                 0x000000C7
#DEFINE EM_CANUNDO              0x000000C6
#DEFINE CFE_PROTECTED           16
#DEFINE SCF_DEFAULT             0
#DEFINE SCF_SELECTION           1
#DEFINE SCF_WORD                2
#DEFINE BULLET_NUMBER           2
#DEFINE CFM_BACKCOLOR           0x04000000
#DEFINE CFM_PROTECTED           16
#DEFINE LF_FACESIZE             0x00000032
#DEFINE PFM_STARTINDENT			0x00000001
#DEFINE PFM_RIGHTINDENT			0x00000002
#DEFINE PFM_OFFSET				0x00000004
#DEFINE PFM_ALIGNMENT			0x00000008
#DEFINE PFM_OFFSETINDENT		0x80000000
#DEFINE PFM_TABSTOPS			0x00000010
#DEFINE PFM_SPACEBEFORE			0x00000040
#DEFINE PFM_SPACEAFTER			0x00000080
#DEFINE PFM_STYLE               0x00000400
#DEFINE PFM_BORDER              0x00000800
#DEFINE PFM_SHADING             0x00001000
#DEFINE PFM_LINESPACING			0x00000100
#DEFINE PFM_NUMBERING           0x00000020
#DEFINE PFM_NUMBERINGSTART      0x00008000
#DEFINE PFM_NUMBERINGSTYLE      0x00002000
#DEFINE PFNS_PAREN              0x000
#DEFINE PFNS_PARENS             0x100
#DEFINE PFNS_PERIOD             0x200
#DEFINE PFNS_PLAIN              0x300
#DEFINE PFNS_NONUMBER           0x400
#DEFINE PFN_BULLET              1
#DEFINE PFN_ARABIC              2
#DEFINE PFN_LCLETTER            3
#DEFINE PFN_UCLETTER            4
#DEFINE PFN_LCROMAN             5
#DEFINE PFN_UCROMAN             6
 
Greg,
Would you be so kind as to give me the code in the frmMatchingText.lblFind.Click() ?
You mentioned that over there you used raiseevent(), since I am not experienced with that syntax I would like to learn from you how to use it please.
And also the method clsCharFormat, here you give me the hint I should look at Christoff's article about API Programming for experienced. Do I have to add the method clsCharFormat to the oleRTF with in the init of that method an AddProperty for crBackcolor, dwMaks etc. etc. ?
It is also not clear to me where/in which method you put the code which starts with
Code:
Local lcCharformat
in this code you also call the various DEFINE's.
And also what are the other values for the CFM_BACKCOLOR, suppose I would like to have black letters on bright yellow background?
Thanks for replying and sorry to bother you again.
Stay healthy
Koen
Stay healthy,
Koen
 
Attached is the RTF Editor class that should have everything you need for the editor.

As for the RAISEEVENT() -- it is in the find form (which is in the attached class).

In my application framework I do not use any modal forms and it is a MDI type application. I wanted to have a 'semi-modal' form that is modal only to the parent MDI form. So if multiple MDI forms are open, the modality is not carried to a different MDI form. To do this, I have a command button that calls the child form on the MDI form and passes as a parameter 'thisform' to the child form; the command button also does BINDEVENT of the child form's Release event to a handler on the MDI form. I have a shadowbox control (basically an image) on the MDI form that is the size of the MDI form and Left=0 and Top=0 (so that it covers the entire MDI form) with Visible=False. In the Init() event of the child form, I store the reference to the parent form into a property called 'MainForm'. The child form then calls the method on the MDI form to invoke the shadowbox control. The shadowbox control does a screen capture (via GDI) of the MDI form and then sets all objects but itself to Visible=False (this retains the appearance of the MDI form). The shadowbox control has in its Activate event code to put the focus back to the child form (this causes the child form to be modal to the parent MDI form; as such, I call it a semi-modal form). Now when the user selects the 'Save' or 'Close' button, I store the information to be passed back to the parent form and pass via the RAISEEVENT() command which allows for multiple parameters to be passed. The event raised is specific to the child form (I name the event OnFormXXXXX where XXXXX is the name of the child form). This OnFormXXXXX method is in the MDI form. It closes the child form (I have a method called ReleaseChildForm) and removes the BINDEVENT to the child form. This allows me to pass more than one parameter to the parent form which is not allowed in normal modal forms.

Hope this explanation is enough to understand...


Greg
 
Greg,

Sorry cant open / run your rtfedito class library: "The ThemeClasses.vcx" is missing.
Would you mind to upload that one as well?
Stay healthy,
Koen
 
Greg,

Now ThemeClasses is complaining "Baseclasses.vcx" is not found.

Stay healthy,

Koen
 
Greg,

Indeed (LOL) However now I am facing the problem I dont have a RTFEdit form - or whatever you call it.
I only have:

RTFclasses_u9if1h.png


neither one gives me a form with oleRTF and your search method.

Hmm, this seems to be more complicated than my first idea and I tend to forget it.

Thanks for your efforts.

Stay healthy,

Koen
 
The editor is a class. Open the RTFTextEditor class. This has the editor that can be placed onto a form. It is self contained. The buttons are located in this class.
 
Greg,

OK a container on a form that makes sense.

Stay healthy,

Koen
 
Greg,

Hmm I have created a form and dropped your container on it.
NOw I have 2 errors:
1) apicreate.prg is missing
2) variable TRUE is not found

cheers.

Stay healthy,

Koen
 
Greg,
What I usualu do in a case like this: I copy all the files I believe which are ness to run the procedure in a seperate directory, than add a little program which CD's to the current directory and which clears the path, set the path to the current directory and run the main form. As a double check I compile the project and watch carefully Fox did not add any more elements (.prg's , .vxc, .frx. .bmp a.s.o) to the project, if so I also add these and I have a running .pjx with all the files ness. Maybe that's an idea also for you since you have so many files involved.
Stay healthy,
Koen
 
The 'variable' True is actually a #DEFINE in my defines file (I have a global file) -- see attached. APICreate is defined in my DeclareWinAPI.prg which declares all the DECLARE-DLL commands -- see attached. I have the command:

SET PROCEDURE TO DeclareWinAPI.prg ADDITIVE

In my main.prg program that starts the application. Using it this way only loads the DECLARE-DLL command when it is actually used. The prefix 'api' indicates that it is a DECLARE-DLL command.


Greg
 
 https://files.engineering.com/getfile.aspx?folder=30d892b0-1c9f-426c-bf63-f0c9b6f2a50f&file=Files.zip
Greg,
The error I get is "apiheapcreate.prg" is missing. In the zip I find "declarewinapi.prg" this file has the apiheapcreate procedure, how ever it won't be found since you hardcoded on the first line of declarewinapi:
#INCLUDE ..\..\common\includes\gkkdefines.h
I will introduce a call to declarewinapi and I will change the hardcoded direcgory for gkkdefines.h and report back

After done that my next error is ; RTFcodes.dbf does not exist

Maybe if you follow my advise and try to run the zip I mailed you in a new2 directory?

Stay healty,
Koen
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top