Tutorial: Using the Text Object Model (TOM) with Pure Basic

Share your advanced PureBasic knowledge/code with the community.
XCoder
User
User
Posts: 68
Joined: Tue Dec 31, 2013 9:18 pm

Tutorial: Using the Text Object Model (TOM) with Pure Basic

Post by XCoder »

The Text Object Model (TOM)
The Text Object Model (TOM) underpins the rich text controls provided for Windows by Microsoft. Some of TOM's methods are available via API calls to the Editor gadget but several useful methods are unavailable that allow:

⦁ toggling the case of text selected in a rich text control
⦁ selecting whole characters, words, sentences, lines and paragraphs
⦁ underlining words with coloured lines and in a range of styles eg red wavy lines
⦁ implementing additional styles for bullet points, for example, numbers in circles
⦁ etc.

All these features can be accessed in PureBasic by setting up a TOM interface. The Microsoft website, https://docs.microsoft.com/en-us/window ... ject-model, contains information about the Text Object Model but there is little detail on how to implement and use it. This tutorial aims to provide a brief introduction to implementing and using the Text Object Model in PureBasic. Programs that demonstrate how to use the Text Object Model are included in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file.

Technical details
Briefly, TOM consists of a hierarchy of objects, of which the root object is defined by the ITextDocument2 interface (this is an extension of an earlier version of the TOM object, ITextDocument). This object has methods for creating objects that are lower in the hierarchy, for example, the ITextRange object that is used for basic text processing. To use rich text formatting, the objects ITextFont and ITextPara can be obtained from an ITextRange object. There is also an ITextFont object to format fonts and an ITextPara object to format paragraphs.

This brief, condensed tutorial will not explore technical details of TOM; instead it provides a practical guide for using some of TOM's methods in PureBasic.

Conventions used in this tutorial
Procedures for the rich text control will begin with Editor_
Procedures that use TOM will begin with TOM_

Using the latest rich text editor control in PureBasic
The TOM interface is only available for rich edit controls with a version number of 3.0 or greater. The Editor gadget provided by PureBasic does not use the latest rich edit control from Microsoft; fortunately, PureBasic is able to load the library that contains the latest rich edit control and thereby access the methods provided by TOM. In Windows 10 the latest rich text edit control is in the file msfted.dll. PureBasic can load this library in Windows 10 using the following procedure:

Code: Select all

Procedure Editor_CreateRTFControl(ParentWindow, x=0, y=0, w=500, h=240)
  ;ParentWindow is the PureBacic window reference eg #MainWindow that will contain the RTF control
  ;x - the x-position of the rich text control
  ;y -  the y-position of the rich text control
  ;w - the width  of the rich text control
  ;h - the height  of the rich text control
  ;RETURNS - the Window's handle for the rich text control

  Protected flags = #WS_CHILD|#WS_VISIBLE|#ES_MULTILINE|#WS_VSCROLL|#ES_NOHIDESEL|#ES_AUTOVSCROLL|#ES_WANTRETURN |#ES_AUTOHSCROLL |#WS_HSCROLL
  OpenLibrary(0, "MSFTEDIT.dll")
  ProcedureReturn CreateWindowEx_(#WS_EX_CLIENTEDGE,"RichEdit50W","",flags, x, y, w, h, WindowID(ParentWindow), 0, GetModuleHandle_(0), 0)  
EndProcedure
Another procedure is required to free the rich text edit control after use:

Code: Select all

Procedure Editor_DestroyRTFControl(hRichEd)
  ;hEditor is the window's handle of the RTF control
  DestroyWindow_(hRichEd)
  CloseLibrary(0)
EndProcedure
Initialising the TOM interface and releasing it in PureBasic
The following procedure must be called to set up the TOM interface. This interface is only available for richedit controls with a version number of 3.0 or greater. The procedure returns #True if successful, otherwise it returns #False. Two global variables must be declared.

Code: Select all

Global RichEditOleObject.IRichEditOle
Global TomDoc2.ITextDocument2

TOM_InitTomInterface2(hRichEd)
  Protected Result
  SendMessage_(hRichEd, #EM_GETOLEINTERFACE, 0, @RichEditOleObject.IRichEditOle)
  If RichEditOleObject
    RichEditOleObject\QueryInterface(?IID_ITextDocument2, @TomDoc2.ITextDocument2)
    RichEditOleObject\Release()
    Result=#True
  Else
    Result=#False
  EndIf 
  ProcedureReturn Result
EndProcedure
The procedure requires the following data that is placed in a DataSection:

Code: Select all

DataSection ;Data for the ITextDocument interface (TOM document)
  IID_ITextDocument2:
  Data.l $C241F5E0
  Data.w $7206,$11D8
  Data.b $A2, $C7, $00, $A0, $D1, $D6, $C6, $B3
EndDataSection
After use, the TOM interface must be released using the following procedure:

Code: Select all

TOM_CleanUpTOM()
  If TomDoc2
    TomDoc2\Release()
  EndIf
EndProcedure
Putting rtf text in the rich text editor control
Text needs to be written in the rich text editor control so that the TOM methods can be demonstrated. The following procedure will be used to achieve this purpose. It inserts text in the rich text editor control at the current insertion point. However, if text is selected in the rich text editor control then this procedure will overwrite that text:

Code: Select all

Procedure Editor_InsertRTFText(hRichEd, rtfText$)
  ;hRichEd - The window handle for the rtf control
  ;rtfText$ - the text to put in the rich text editor; this must start with {\rtf if the text is in rtf format
  Protected RTFbuffer, st.SETTEXTEX
  #ST_SELECTION = 2
  RTFbuffer = AllocateMemory(Len(rtfText$)+1)
  If RTFbuffer
    PokeS(RTFbuffer, rtfText$, #PB_Any, #PB_Ascii)
    st\flags = #ST_SELECTION
    st\codepage = #CP_ACP
    SendMessage_(hRichEd, #EM_SETTEXTEX, @st, RTFbuffer)
    FreeMemory(RTFbuffer)
  EndIf
EndProcedure
Errors
Most of the methods for the ITEXTRANGE object (discussed later) return an error code if an issue is detected. The following procedure is useful for returning an error message if an error is detected, or to indicate that an error did not occur:

Code: Select all

Procedure TOM_GiveErrorMessage(result)
  If result <> #S_OK
    Select Result
      Case #E_INVALIDARG:    Debug ("E_INVALIDARG- invalid argument")
      Case #E_ACCESSDENIED:  Debug ("E_ACCESSDENIED - write access denied")
      Case #E_OUTOFMEMORY:   Debug ("E_OUTOFMEMORY - out of memory")
      Case #CO_E_RELEASED:   Debug ("CO_E_RELEASED - the paragraph formatting object is attached to a range that has been deleted.")
      Default: Debug "Some other error occurred"
    EndSelect
  Else
    Debug "No error"
  EndIf
EndProcedure
All these procedures are in the module called TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file.

The unit TOM.pbi in the zip file contains all the code above. It also includes several constants that is needed by TOM.

Using TOM in PureBasic
Having set up the module called TOM.pbi we can implement features that are not available with normal API functions.

Changing the case of selected text
The case of text selected in a rich text control can be amended by using the method ChangeCase provided by TOM. Although the rich text control has methods that facilitate some case changes (lower case/upper case), TOM facilitates three additional case changes:

⦁ title case (to capitalise the first letter of each word in selected text)
⦁ sentence case (to capitalise the first letter of each sentence in selected text)
⦁ toggle case (to change the case of the characters in selected text).

PureBasic needs to define the following constants to use this feature. These constants have been placed in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file.

Code: Select all

#tomLowerCase = 0
#tomUpperCase = 1
#tomTitleCase = 2
#tomSentenceCase = 4
#tomToggleCase = 5
Here is a procedure to change the case of selected text using the method ChangeCase:

Code: Select all

Procedure TOM_ChangeCase1()
  Protected TextRange.ITEXTRANGE, result
  result = TomDoc2\GetSelection(@TextRange)  ;retrieve an ITEXTRANGE object from TomDoc2 and get the range of the selected text
  TOM_GiveErrorMessage(result)
  result = TextRange\ChangeCase(#tomToggleCase) ;Change the case of the text identified by TextRange
  TOM_GiveErrorMessage(result)
EndProcedure
The procedure calls the GetSelection() method of TomDoc2 to retrieve an ITEXTRANGE object from TomDoc2 (TextRange) and get and store information about the selected text, including its start and end positions.
TomDoc2\GetSelection(@TextRange) returns a result code that is passed to TOM_GiveErrorMessage() as an argument and this displays the outcome in a debug window. The ChangeCase() method of the ITEXTRANGE object is called next; this changes the case of the selected text and returns a result code. The result code is passed to TOM_GiveErrorMessage() and this displays the outcome in a debug window.

Fields and methods of ITEXTRANGE are listed at https://docs.microsoft.com/en-us/window ... itextrange

The program 01 TOM_ChangeCase.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Selecting sections of text above the cursor
Individual characters, whole words, whole sentences, whole lines and whole paragraphs that contain the cursor can be selected using TOM.

PureBasic needs to define the following constants to use this feature. These constants have been placed in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file .

Code: Select all

#tomCharacter = 1 ;Character
#tomWord = 2 ;Word
#tomSentence = 3 ;Sentence
#tomParagraph = 4 ;Paragraph
#tomLine = 5 ;Line (on screen)
#tomStory = 6 ;Story
The following procedure selects the word above the cursor (this includes any spaces immediately following the end of the word).

Code: Select all

Procedure TOM_SelectWordAboveCursor()
  Protected TextRange.ITEXTRANGE, result
  TomDoc2\GetSelection(@TextRange)
  result = TextRange\Expand(#tomWord, 0)
  TOM_GiveErrorMessage(result)
EndProcedure
The program 02 TOM_Selection.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Getting the start and end positions of the text above the cursor
This procedure shows how to get the start and end positions of the word above the cursor.

Code: Select all

Procedure TOM_SelectWordAboveCursor()
  Protected TextRange.ITEXTRANGE, startPos, endPos, result
  TomDoc2\GetSelection(@TextRange)
  TextRange\Expand(#tomWord, 0)
  TextRange\GetEnd(@endPos)
  TextRange\GetStart(@startPos)

  Debug startPos
  Debug endPos
EndProcedure
As in the previous procedure, this calls the GetSelection() method of TomDoc2 to retrieve an ITEXTRANGE object from TomDoc2 (TextRange) and get and store information about the selected range of text. In this case, the range is just the position of the cursor in the text.
The instruction TextRange\Expand(#tomWord, 0) causes the range of the selection to be expanded to cover the word above the cursor. This returns a result code that is passed as an argument to TOM_GiveErrorMessage() and this displays the outcome in a debug window.
This selection will include any spaces immediately following the text above the cursor, but not punctuation marks.
By replacing #tomWord with one of the other constants listed above, you can make other selections eg the current line.

The ITEXTRANGE object stores the start and end positions of the selected text. These positions may be retrieved using the methods GetStart() and GetEnd(). These take a parameter that is the address of the variable in which the start and end addresses are to be placed.

The program 03 TOM_Selection-position.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Retrieving the line of text above the cursor
You can retrieve the line of text above the cursor as the following procedure demonstrates.

Code: Select all

Procedure TOM_GetTextAboveCursor()
  Protected TextRange.ITEXTRANGE, *bStr, theString$, result
  TomDoc2\GetSelection(@TextRange)
  TextRange\Expand(#tomLine, 0) ;#tomWord, #tomLine, #tomParagraph etc may be used here
  result = TextRange\GetText(@*bStr)
  TOM_GiveErrorMessage(result)
  theString$ = PeekS(*bStr, -1, #PB_Unicode) 
  SysFreeString_(*bStr)
  Debug "[" + #PB_Compiler_Filename + " "+ #PB_Compiler_Line +"] Word above cursor: " + theString$
EndProcedure
This procedure selects the line of text containing the cursor.
The text is retrieved using the GetText() method of the ITEXTRANGE object. Memory is automatically allocated for the text by this object.
GetText() returns a pointer to a string data type BSTR (Basic string or binary string) that points to an area of memory that contains the required text. To get the text, the address of a pointer (*bStr) must be passed as an argument to the GetText() method. This pointer will point to the area of memory that contains the required text. The string can be copied to a normal string by using PeekS() as demonstrated in the procedure.
The pointer used by GetText() must be freed manually by the program by calling SysFreeString_()

See https://docs.microsoft.com/en-us/previo ... tomat/bstr for more information on the string data type BSTR
See https://docs.microsoft.com/en-us/cpp/at ... ew=vs-2019 for details of Allocating and Releasing Memory for a BSTR.

The program 04 TOM_GetSelectedText.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Disable\Enable the Undo and Redo facility in a rich text control
By default, the rich text control facilitates redo/undo actions. The TOM contains methods that can disable/enable this facility.

PureBasic needs to define the following constants to use this feature. These constants have been placed in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file

Code: Select all

#tomSuspend = $FF676985
#tomResume  = $FF676986
The following procedure will disable the Undo\Redo facility in a rich text control.

Code: Select all

Procedure TOM_StopUndoRedo()
    TomDoc2\Undo(#tomSuspend, 0) ; Suspends undo recording
EndProcedure 
The following procedure will enable the undo/redo facility in a rich text control:

Code: Select all

Procedure TOM_EnableUndoRedo()
  TomDoc2\Undo(#tomResume, 0) ; Enable undo recording
EndProcedure
The program 05 TOM_Undo_Redo.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Temporarily freezing the rich text control
It is possible to freeze the displayed text in the rich text control so that its contents may be updated by the program without the user seeing any flickering and without loss of performance. Key presses sent to the rich text control while the display is frozen will be processed when the display is released from its frozen state.

PureBasic needs to define the following constants to use this feature. These constants have been placed in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file

Code: Select all

#tomSuspend = $FF676985
#tomResume  = $FF676986
The following procedure will freeze the display:

Code: Select all

Procedure TOM_Freeze()
    TomDoc2\Freeze(0)
EndProcedure
The following procedure will unfreeze the display:

Code: Select all

Procedure TOM_Unfreeze()
    TomDoc2\Unfreeze(0)
EndProcedure
The program 06 TOM_Freeze.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Paragraph formatting - Bullet points
Rich text editors provide several styles for numbering; accessing TOM provides additional styles eg numbers in circles.

PureBasic needs to define the following enumerated constants to use this feature. These constants have been placed in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file

Code: Select all

Enumeration bullets
#tomListNone = 0
#tomListBullet
#tomListNumberAsArabic				;Normal numbering (0, 1, 2,...)followed by a round bracket 
#tomListNumberAsLCLetter			 ;Lower case 'numbering' followed by a round bracket 
#tomListNumberAsUCLetter			 ;Upper case 'numbering' followed by a round bracket
#tomListNumberAsLCRoman			  ;Lower case Roman numbering followed by a round bracket
#tomListNumberAsUCRoman			  ;Upper case Roman numbering followed by a round bracket
#tomListNumberAsSequence
#tomListNumberedCircle				    ;Number placed in plain circle
#tomListNumberedBlackCircleWingding	;Number placed in black circle
#tomListNumberedWhiteCircleWingding	;Seems to be the same as #tomListNumberedCircle
#tomListNumberedArabicWide			   ;Normal numbering with larger line spacing
EndEnumeration
It also needs the following constants to set the decoration around the number:

Code: Select all

#tomListParentheses = $10000 ;Puts round brackets around the number
#tomListPeriod = $20000 ;Puts a full stop after the number
#tomListPlain = $30000 ;Uses plain numbering without brackets of full stops
#tomListMinus = $80000 ;Puts a hyphen after the number
The constants in this list can be OR'd with the constants in the enumerated list.

The following procedure will number the paragraph containing the cursor, placing the number in a circle:

Code: Select all

Procedure TOM_NumberParagraph()
  Protected TextRange.ITEXTRANGE, TextPara.ITEXTPARA, result
  TomDoc2\GetSelection(@TextRange)
  TextRange\GetPara(@TextPara)
  TextPara\SetListType(#tomListNumberedCircle | #tomListPlain)
EndProcedure
The method GetSelection(@TextRange) initialises the ITEXTRANGE object with data about the currently selected text (if no text is selected this will be the position of the cursor).
Calling the method GetPara(@TextPara)initialises the ITEXTPARA object. Calling the method SetListType() of the ITEXTPARA object sets the style of the numbered list for the paragraph. By default, the numbering style set by SetListType() will include a round bracket after the number. This can be removed by ORing the TOM constant that sets the style with #tomListPlain. In this procedure, the tom constant #tomListNumberedCircle is OR'd with #tomListPlain to prevent a round bracket appearing after the circled number.

Experiment shows that ORing the ASCII code for '*' with #tomListNumberAsArabic will provide asterisked bullet points:

TextPara\SetListType(#tomListNumberAsArabic | $2A)

Possibly other symbols can be used as bullet points....

The program 07 TOM_ParagraphNumbering.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.


Paragraph formatting - other
The previous procedure can be extended to provide additional paragraph formatting eg paragraph spacing. Although this can be achieved by sending normal messages to the Editor Gadget, I am including this here as a demonstration of what can be achieved directly with TOM. Use the following TOM constants to set paragraph spacing (included in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file):

Code: Select all

#tomLineSpaceSingle		   =     0 ;Normal (Single) line spacing
#tomLineSpace1pt5        	=     1 ;Line spacing of 1.5
#tomLineSpaceDouble      	=     2 ;Double line spacing
#tomLineSpaceAtLeast     	=     3 
#tomLineSpaceExactly     	=     4 
#tomLineSpaceMultiple    	=     5 
These constants are used with the SetLineSpacing method of the ITEXTPARA object. This method requires two arguments: the first argument is one of the above constants to indicate the type of line spacing to use (the line spacing rule); the second argument is a float value that sets the value of the line spacing (this does not always seems to be used but must be included when calling this method).

The following procedure will put a bullet point at the start of the selected paragraph(s) and set the paragraphs to use double line spacing:

Code: Select all

Procedure DoubleSpacedParagraph()
  Protected TextRange.ITEXTRANGE, TextPara.ITEXTPARA, result
  TomDoc2\GetSelection(@TextRange)
  TextRange\GetPara(@TextPara)
  TextPara\SetLineSpacing(#tomLineSpaceDouble, 0) ;Sets double line spacing
  TextPara\SetListType(#tomListBullet ) ; Puts a bullet point at the start of the paragraph
EndProcedure
The program 08 TOM_Paragraph spacing.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Get the character, word, line, sentence or paragraph index.
This may be useful, for example, if you want to display line numbers in a rich text editor where the height of each line may be different when the height of fonts change on different lines. The line numbers could be displayed in a separate rich text editor next to the one that contains the text. You could get the paragraph index and display this in a status bar as paragraph number.

PureBasic needs to define the following constants to use this feature. These constants have been placed in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file

Code: Select all

#tomCharacter = 1 ;Character
#tomWord = 2 ;Word
#tomSentence = 3 ;Sentence
#tomParagraph = 4 ;Paragraph
#tomLine = 5 ;Line (on screen)
To get the index for one of these items, the GetIndex() method of the ITEXTRANGE is required. This has two parameters. The first is one of the above constants to tell the object which index is required. The second is the address of a variable that will store the index.

The following procedure will obtain the index of the line containing the cursor.

Code: Select all

Procedure TOM_GetIndex()
  ;Gets the index of the line at the cursor position (index starts at 1)
  Protected TextRange.ITEXTRANGE, valIndex, result
  TomDoc2\GetSelection(@TextRange)
  result = TextRange\GetIndex(#tomLine, @valIndex) ;Gets line number that cursor is in, starting at 1
  If result <> #S_OK : TOM_GiveErrorMessage(result): EndIf
  Debug "The index of the line containing the cursor is "+Str(valIndex)
EndProcedure
The index is obtained using the GetIndex()method of the ITEXTRANGE object. This has two parameters. The first is one of the TOM constants to tell the ITEXTRANGE object which index is required (character, word, sentence, etc). The second is the address of a variable that will store the index.

The GetIndex()method returns an error code that can be sent to the TOM_GiveErrorMessage() procedure in the unit TOM.pbi. If there is no error then it returns #S_OK. The procedure examines the returned code and only calls TOM_GiveErrorMessage() is an error code is returned.

When indexing words, End of Line markers and a full stop is included in the result.

The program 09 TOM_indexing.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Underlining text
TOM may be used to underline words using a wide variety of different underlining styles. For example, it is possible to underline text with dotted lines, dashed lines, and wavy lines. The latter are used in word processors to indicate spelling mistakes.

PureBasic needs to define the following constants to use this feature. These constants have been placed in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file

Code: Select all

#tomNone            		= $00000000
#tomSingle           	  = $00000001
#tomWords           		= $00000002
#tomDouble          		= $00000003
#tomDotted          		= $00000004
#tomDash            		= $00000005
#tomDashDot         		= $00000006
#tomDashDotDot      		= $00000007
#tomWave            		= $00000008
#tomThick           		= $00000009
#tomHair            		= $0000000A
#tomDoubleWave      	   = $0000000B
#tomHeavyWave       	   = $0000000C
#tomLongDash        		= $0000000D
#tomThickDash       		= $0000000E
#tomThickDashDot		    = $0000000F
#tomThickDashDotDot	    = $00000010
#tomThickDotted		     = $00000011
#tomThickLongDash		   = $00000012
The following procedure demonstrates how to underline the word above the cursor with a red wavy line.

Code: Select all

Procedure TOM_UnderlineWord()
  Protected TextRange.ITEXTRANGE,TextFont.ITEXTFONT
  TomDoc2\GetSelection(@TextRange)
  TextRange\Expand(#tomWord, 0)
  TextRange\GetFont(@TextFont)
  TextFont\SetUnderline($0600+#tomWave) ;$0600 = Red #tomWave = $0008
  TextFont\Release()
EndProcedure
The procedure first gets the word under the cursor.
The method GetFont(@TextFont) initialises an ITEXTFONT object.
Calling the method SetUnderline() draws the coloured line in the chosen style. The parameter passed to this method consists of the value for the colour of the line to which a value for the style of the line (#tomWave) is added.
The last line releases the ITEXTFONT object.

This procedure demonstrates how to remove the wavy line:

Code: Select all

Procedure TOM_RemoveUnderline()
  Protected TextRange.ITEXTRANGE,TextFont.ITEXTFONT
  TomDoc2\GetSelection(@TextRange)
  TextRange\Expand(#tomWord, 0)
  TextRange\GetFont(@TextFont)
  TextFont\SetUnderline(#tomNone)
  TextFont\Release()
EndProcedure
The procedure is similar to that for underlining a word except that the parameter for SetUnderline is #tomNone.

The program 10 TOM_UnderlineWave.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Issue with words that are already underlined
Documents may contain words that are underlined using black straight lines - the normal underlining style. If the first procedure is used to underline a word that already has the normal underlining style then when the second procedure runs it will not restore the original underlining style - it removes all underlining from the word. This could cause a problem in a word-processing program that underlines misspelt words with a red wavy line. When the word is corrected then the original underlining will disappear - it would be necessary to keep track of every underlined word so that the original underlining can be restored to these words. Even worse, if the document contains valid words or expressions that are not in the dictionary (eg program code or mathematical terms) then these will be identified as being misspelt and when the document is saved the red wavy lines will be saved with the document!

Fortunately The TOM provides an alternative way of underlining words that resolves this problem; with TOM it is possible to use temporary underlining (other temporary styles can be applied to characters too if required). When temporary underlining is removed the original underlining style (and character style) is restored; moreover, temporary underlining is not stored in the document file when the document is saved.

Implementing temporary underlining
PureBasic needs to define the following constants to use this feature. These constants have been placed in the module TOM.pbi in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file

Code: Select all

#tomUndefined = $FF676981
#tomDefault = $FF676984   
#tomApplyNow = $00000000 
#tomApplyLater = $00000001
#tomTrackParms = $00000002
#tomCacheParms = $00000003
#tomApplyTmp = $00000004
This procedure shows how to implement temporary underlining. It will underline the word above the cursor with a temporary blue wavy line.

Code: Select all

Procedure TOM_TemporaryUnderlineWord()
  Protected TextRange.ITEXTRANGE, TextFont.ITEXTFONT
  TomDoc2\GetSelection(@TextRange)
  TextRange\Expand(#tomWord, 0)
  TextRange\GetFont(@TextFont)
  TextFont\Reset(#tomApplyTmp) ;Use temporary formatting
  TextFont\SetUnderline(#tomWave)
  TextFont\SetUnderline($FFFF0000);Set the underline colour to blue
  TextFont\Reset(#tomApplyNow)
  TextFont\Release()
EndProcedure
After setting up the ITEXTFONT object, its method Reset() is called with the argument #tomApplyTmp to use temporary formatting. This method is called again to set the colour for underlining words. In this case the argument $FFFF0000 is used. The last six digits control the colour of the line (These seem to be the colour values for RGB in reverse, so for a red line use $FF0000FF).
Then the method Reset() is called with the argument #tomApplyNow to apply the underlining.
Finally, the ITEXTFONT object is released by calling its method Release()

The next procedure shows how to remove the temporary underlining from the word above the cursor in the Editor.

Code: Select all

Procedure TOM_RemoveUnderline()
  Protected TextRange.ITEXTRANGE, TextFont.ITEXTFONT
  TomDoc2\GetSelection(@TextRange)
  TextRange\Expand(#tomWord, 0)
  TextRange\GetFont(@TextFont)
  TextFont\Reset(#tomApplyTmp)
  TextFont\SetUnderline(#tomNone) ;Remove temporary formatting
  TextFont\Reset(#tomApplyNow)
  TextFont\Release()
EndProcedure
After selecting the word above the cursor, the ITEXTFONT object is set up by the code TextRange\GetFont(@TextFont).
The reset() method of this object is called with the argument #tomApplyTmp to put the object into temporary formatting mode.
The line SetUnderline(#tomNone) tells the object that we want to remove temporary formatting.
The line TextFont\Reset(#tomApplyNow) removes the temporary formatting.
Finally, the ITEXTFONT object is released by calling its method Release().

The program 11 TOM_TemporaryUnderlineWave.pb in the zip file at http://www.mediafire.com/file/w24uw0ncu ... M.zip/file demonstrates this procedure.

Comments
Although PureBasic's gadget editor does not use the latest rich edit control, PB can load the latest rich text edit control that contains the latest TOM objects and its auto-complete facility will list some of the methods available with TOM objects. For example, as you type the following, a list of some methods for the ITEXTRANGE object are shown in a drop down box after the '\' is typed.

TextRange.ITEXTRANGE
TextRange\

Pure Basic's auto-complete facility does not show all the methods available for the objects; some 'missing' TOM methods could be very useful particularly those for handling tables (including inserting rows and columns, deleting rows and columns, and colouring rows and columns using different colours) and implementing mathematical equations/chemical formulae.

Editor gadget with spell checker
I will post a program that demonstrates how to do spell checking in a rich text editor that uses some of the objects and methods in this tutorial at a later date.
Last edited by XCoder on Sun Nov 01, 2020 2:43 pm, edited 1 time in total.
User avatar
blueb
Addict
Addict
Posts: 1044
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: Tutorial: Using the Text Object Model (TOM) with Pure Ba

Post by blueb »

Thanks Xcoder.

Downloaded the TOM.ZIP file and it includes everything but the "Tom.pbi" file.

Perhaps I'm missing something? :?
- It was too lonely at the top.

System : PB 6.10 LTS (x64) and Win Pro 11 (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
XCoder
User
User
Posts: 68
Joined: Tue Dec 31, 2013 9:18 pm

Re: Tutorial: Using the Text Object Model (TOM) with Pure Ba

Post by XCoder »

XCoder wrote:Downloaded the TOM.ZIP file and it includes everything but the "Tom.pbi" file.
Apologies, I missed this file out of the zip file. I have updated the zip file here:

http://www.mediafire.com/file/w24uw0ncu ... M.zip/file
Karellen
User
User
Posts: 82
Joined: Fri Aug 16, 2013 2:52 pm
Location: Germany

Re: Tutorial: Using the Text Object Model (TOM) with Pure Ba

Post by Karellen »

Wow, this is really interesting. Very cool, thanks!
Stanley decided to go to the meeting room...
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5357
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Tutorial: Using the Text Object Model (TOM) with Pure Ba

Post by Kwai chang caine »

Works nice here
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
XCoder
User
User
Posts: 68
Joined: Tue Dec 31, 2013 9:18 pm

Re: Tutorial: Using the Text Object Model (TOM) with Pure Ba

Post by XCoder »

I have developed a small spelling checker that uses TOM at viewtopic.php?f=12&t=76205
Post Reply