Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

cRichEdit - Standard RichEdit control for AutoHotkey scripts


  • Please log in to reply
110 replies to this topic
corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004
With all the exciting new options available for AutoHotkey these days I thought I'd start coding functions for using a RICHEDIT control in an AutoHotkey GUI for fun :) . The functionality is pretty basic so far but could be useful to someone. If anyone is interested in trying it out and providing feedback I'd appreciate it :) . This version uses riched20.dll (or riched32.dll when run in Win95) which should already exist in Win98/ME/NT/2000/XP/Vista.

Version 0.09 Beta.
A zip file that contains the necessary files and demo scripts can be downloaded here: Download.

Posted Image


Recent changes:[*:1pa4kl8w]AutoHotkey v1.0.47 or greater required
[*:1pa4kl8w]added: WordWrap On/Off
[*:1pa4kl8w]modified: fixed a bug in cGUI.ahk for specifying a HWND for the control's parent
[*:1pa4kl8w]added: tab_example.ahk example file to show one method of adding the RichEdit control to a tab
[*:1pa4kl8w]added: GetLineNum - to retrieve the current line number, the total number of lines or the line number at a specific character index
[*:1pa4kl8w]fixed: bug with specifying a colour by name
[*:1pa4kl8w]added: Align (Left, Right, Center)
[*:1pa4kl8w]added: ReplaceSel - to insert or replace text at the current position
[*:1pa4kl8w]added: GetRTF - to retrieve RTF code for the current selection or contents of the RichEdit control
[*:1pa4kl8w]added: an option to cGUI to specify the HWND to the parent control (currently untested)
[*:1pa4kl8w]modified: removed dependency on Insert/Extract Integer functions (now uses NumPut, NumGet instead)
[*:1pa4kl8w]updated: demo scripts (simple, feature)
[*:1pa4kl8w]modified: method used for colour names, minor bug fixes/tweaks
[*:1pa4kl8w]modified: split the include files into cGUI.ahk and cRichEdit.ahk since cGUI.ahk might be used for other controls in the future.
[*:1pa4kl8w]renamed: cGUI_RICHEDIT function to c_RichEdit() instead (easier to remember and less to type)
[*:1pa4kl8w]modified: feature_demo.ahk to use a ListView to list/test commands instead of the buttons that were used previously. This method seems to allow more flexibility when adding/modifying/testing new options
[*:1pa4kl8w]added: FontName _action to specify a font to use by name
[*:1pa4kl8w]added: GetFontSize _action to get the font size of the current selection
[*:1pa4kl8w]added: SetFontSize _action to set the font size of the current selection
[*:1pa4kl8w]added: FontStyle _action to set/toggle Bold, Italic, UnderLine, Strikethrough for the current selection or current position (optional custom dwMask and dwEffects settings available also)
[*:1pa4kl8w]added: FontColor _action to change the colour of selected text or change the colour at the current location (RRGGBB value or by name - same names/values as Object Colors in the AutoHotkey documentation)
[*:1pa4kl8w]updated: BackColor _action to support colour names also
[*:1pa4kl8w]updated: feature_demo.ahk script to test some of the newer features that have been added
[*:1pa4kl8w]Added the following to the cGUI_RICHEDIT() function:[*:1pa4kl8w]EXGETSEL - retrieves the starting and ending character positions of the selection
[*:1pa4kl8w]EXSETSEL - set the current selection
[*:1pa4kl8w]GETSELTEXT - get the current text that is selected in the control
[*:1pa4kl8w]GETTEXTLENGTH - get the number of characters in the control
[*:1pa4kl8w]GETTEXT - get the text in the control
[*:1pa4kl8w]BackColor - change the background colour of the RichEdit control
[*:1pa4kl8w]SETSCROLLPOS - scroll to a specific position in the control
[*:1pa4kl8w]GETSCROLLPOS - get the current scroll position in the control
[*:1pa4kl8w]AutoURLDetect - automatic detection of URLs[*:1pa4kl8w]fixed hInstance usage [thanks PhiLho]
[*:1pa4kl8w]fixed: RICHEDIT was hard coded [thanks PhiLho]
[*:1pa4kl8w]added FreeDlls option (_action param) to cGUI() to free all dlls loaded by the cGUI() function
[*:1pa4kl8w]updated demo to load RichEdit v2.0
[*:1pa4kl8w]modifed global variables used
[*:1pa4kl8w]Added simple_demo.ahk and feature_demo.ahk scripts to the zip file available for download
[*:1pa4kl8w]modified demo scripts to load a RichEdit1.0 control instead if run under Win95[/list]**Note to Win95 users: Win95 can load a RichEdit 2.0 control but the file riched20.dll is not included in a standard install and would have to be downloaded and installed separately.

Here's the code for a sample script (simple_demo.ahk - screenshot). The code for feature_demo.ahk is available in the zip file.
; v0.06 beta - updated: June 24, 2007

; **** Download the sample .rtf file if necessary ****
If !(FileExist(A_ScriptDir . "\test1.rtf")) {
  SplashTextOn, 300, 30, !, Downloading sample file. Please wait...
  URLDownloadToFile https://ahknet.autohotkey.com/~corr/test1.rtf, %A_ScriptDir%\test1.rtf
  SplashTextOff
}  
  
; **** RichEdit Demo ****

Gui Add, Button, gOpenScript, Open Script
Gui Add, Button, ym gHelloWorld, Hello World!!
Gui Add, Button, ym gtest1, test1.trf
Gui Show, +hide w500 h500, RichEdit demo
If A_OSVersion = WIN_95
  cGUI("Add", REdit1, 10, 40, 480, 450, "RICHEDIT")
Else
  cGUI("Add", REdit1, 10, 40, 480, 450, "RichEdit20A")
cRichEdit(REdit1, "FileOpen", (A_ScriptDir . "\test1.rtf"))
Gui Show
Return

test1:
cRichEdit(REdit1, "FileOpen", (A_ScriptDir . "\test1.rtf"))
Return

OpenScript:
cRichEdit(REdit1, "FileOpen", A_ScriptFullPath)
Return

HelloWorld:
cRichEdit(REdit1, "Text", "Hello World!!")
Return

GuiClose:
If (REdit1) 
  cRichEdit(REdit1, "Destroy")
Gui, %A_Gui%:Destroy
cGUI("FreeDlls", NULL)
ExitApp
Return

; comment the following #Include lines if you are using stdlib and have copied the required
; cGUI.ahk and cRichedit.ahk files to your stdlib directory

#Include cGUI.ahk
#Include cRichEdit.ahk
I'll be adding additional functionality to the cRichEdit() function with each release. If you'd like to help out then feel free to add to the function and post your changes so that I can update the code in the first post.

More to come soon... Enjoy :)

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Very nice! Thanks for sharing it!

It shows Unicode characters, so we can write a substitute of MsgBox in a few lines of additional code, with nicely formatted, diacritical letters, symbols.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
I am waiting for the other functions, most notably those that change fonts and do other rich edit specific functions.

Now that you are doing it, I will put my own RE wrapper on hold.

Thx.
Posted Image

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Splendid :D Thanks for creating this.

:)

Elevator_Hazard
  • Members
  • 297 posts
  • Last active: Feb 07 2011 12:10 AM
  • Joined: 28 Oct 2006
Looks nice. How about instead of 4 parameters being taken up and sometimes causing a bunch of commands and confusion, you should have the width height x and y in an options parameter so that support could also easily be added to like an associated output variable and a subroutine if that's possible. Also I haven't tried this out yet, but it looks good and I had been waiting for something like that.
Changed siggy at request of ahklerner :D

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Jun 30 2014 07:48 PM
  • Joined: 04 Dec 2006

*/
If _action = Text
{
DllCall("SendMessage", "UInt", _ctrlID, "UInt", 0x461, "Str", "", "Str", opt1) ; EM_SETTEXTEX
Return
}
/*


likely there is reason to use DLL call for sendmessage (vs. built in sendmessage)...


curious as to what it is....
Joyce Jamce

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

Very nice! Thanks for sharing it!

It shows Unicode characters, so we can write a substitute of MsgBox in a few lines of additional code, with nicely formatted, diacritical letters, symbols.

Thanks :) . According to MSDN, version 1.0 of the RichEdit control does not support unicode but since riched32.dll on XP seems to be a stub, it's likely not a version 1.0 control that gets loaded. Win9.x systems will not likely have the same results using the same code.

ANSI (single-byte character set (SBCS) and multibyte character set (MBCS)) editing However, there is no Unicode editing.



corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

I am waiting for the other functions, most notably those that change fonts and do other rich edit specific functions.

The next update should contain a few text formatting features :) .

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

Splendid :D Thanks for creating this.

:)

Thanks for giving it a try :) .

Looks nice. How about instead of 4 parameters being taken up and sometimes causing a bunch of commands and confusion, you should have the width height x and y in an options parameter so that support could also easily be added to like an associated output variable and a subroutine if that's possible. Also I haven't tried this out yet, but it looks good and I had been waiting for something like that.

Thanks :) . I had thought about setting the options up similar to the Gui command but didn't mainly for a couple reasons. There are many options available for a RichEdit control and many of them may require additional params. It seemed easier and a bit more flexible to create a separate function for additional settings, actions, etc... instead of parsing through a single line of text to pull out and process all available options. I would have preferred to use VB style and create properties and methods for the control but that's not going to happen with this release... The other reason for the x,y,w,h being separate params is to allow the use of expressions.

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

likely there is reason to use DLL call for sendmessage (vs. built in sendmessage)...

The main reason is for easy porting of code (viewing, order of params) from other projects and snippets. There doesn't seem to be a need to use the built-in SendMessage/PostMessage commands since it's not necessary to search for a Window Title, Text, etc... since the control's HWND has already been passed to the function. Unless the built-in SendMessage/PostMessage turns out to be considerably faster (I haven't tested)...

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

Unless the built-in SendMessage/PostMessage turns out to be considerably faster (I haven't tested)...

I doubt it, unless the DllCall adds lot of overhead... Which can be reduced by specifying explicitly the DLL and using SendMessageA (avoids some internal tries).
Additional advantage: you probably don't need the DetectHiddenWindows On.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

Daniel2
  • Guests
  • Last active:
  • Joined: --
corrupt- This is really awesome! This has been a highly anticipated gui control.. & I'm looking forward to it being fully implemented. I'm new to trying to make dllcalls.. but thanks to PhiLho's help (ApiViewer really helped w/ making it all click! :)) I managed to wrap up a couple new functions:

EM_EXGETSEL - retrieves the starting and ending character positions of the selection in a RE control.cGUI_RICHEDIT(ReHwnd, "EM_GETTEXTRANGE")EM_SETFONTSIZE - The EM_SETFONTSIZE message sets the font size for the selected text.cGUI_RICHEDIT(ReHwnd, "EM_SETFONTSIZE", -4) ; (changes font size from current state..)(requires ExtractInteger & InsertInteger functions to be available as shown in 'Structures and Arrays')

/*
*****************************************************************************************
Add your own RICHEDIT features to the function below

Please submit additional features if you think that others may find
them useful so that I can update the current version posted 

Much more to come soon...
*****************************************************************************************
*/
  Else If _action = YourNewFeature
  {
    ; Do something here
    Return
  } ;-----------------------------
  Else If _action = EM_EXGETSEL
  {
;-- The EM_EXGETSEL message retrieves the starting and ending character positions of the selection
    VarSetCapacity(_tmp1, 8, 0)     , InsertInteger(8, _tmp1, 4)
    DllCall("SendMessage"                       ;// returns LRESULT in lResult
                    , "UInt"        , _ctrlID   ;// handle to destination control
                    , "UInt"        , 0x434     ;// message ID   (EM_EXGETSEL := WM_USER + 52)
                    , "UInt"        , 0         ;// wParam  - Parameter is not used; must be zero.
                    , "Cdecl UInt"  , &_tmp1)   ;// lParam  - Pointer to CHARRANGE structure that receives the selection range
    Return ExtractInteger(_tmp1, 0) . "|" . ExtractInteger(_tmp1, 4)  ;// Return Value: returns CHARRANGE Structure (cpMin & cpMax)
  } ;-----------------------------
  Else If _action = EM_SETFONTSIZE
  {
;-- The EM_SETFONTSIZE message sets the font size for the selected text.--
    DllCall("SendMessage"                       ;// returns LRESULT in lResult
                    , "UInt"        , _ctrlID   ;// handle to destination control
                    , "UInt"        , 0x4df     ;// message ID   (EM_SETFONTSIZE := WM_USER + 223)
                    , "UInt"        , opt1      ;// wParam  - Change in point size of the selected text (–1637 to 1638)
                    , "Cdecl UInt"  , 0)        ;// lParam  - This parameter is not used; it must be zero.
    Return          ;// Return Value:  This message does not return a value.
  } ;-----------------------------
;   Else If _action = EM_GETSELTEXT
;   {
; ;-- The EM_GETSELTEXT message retrieves the starting and ending character positions of the selection in a rich edit control.
;     DllCall("SendMessage"                       ;// returns LRESULT in lResult
;                     , "UInt"        , _ctrlID   ;// handle to destination control
;                     , "UInt"        , 0x43e     ;// message ID   (EM_GETSELTEXT := WM_USER + 62)
;                     , "UInt"        , 0         ;// wParam  - This parameter is not used; it must be zero.
;                     , "Cdecl Str"   , &_tmp1)   ;// lParam  - Pointer to a buffer that receives the selected text. The calling
;                                                 ;             application must ensure that the buffer is large enough to hold
;                                                 ;              the selected text.
;     Return _tmp1  ;// Return Value: Message returns the # of chars copied, not including the terminating null character
;   } ;-----------------------------
;   Else If _action = EM_GETTEXTRANGE
;   {
; ;-- The EM_GETTEXTRANGE message retrieves a specified range of characters from a rich edit control.
;     VarSetCapacity(_tmp1, 512, 0)   , InsertInteger(2, _tmp1, 4)
;     InsertInteger(10, _tmp1, 8)     , InsertInteger(_tmp1, _tmp1, 12)
;     _tmp2 := DllCall("SendMessage"              ;// returns LRESULT in lResult
;                     , "UInt"        , _ctrlID   ;// handle to destination control
;                     , "UInt"        , 0x44b     ;// message ID   (EM_GETTEXTRANGE := WM_USER + 75)
;                     , "UInt"        , 0         ;// wParam  - This parameter is not used; it must be zero.
;                     , "Cdecl Str"   , &_tmp1)   ;// lParam  - Pointer to a TEXTRANGE structure; specifies the range of
;                                                 ;             characters to retrieve and a buffer to copy the characters to.
;     Return ExtractInteger(_tmp1, 0) . "|" . ExtractInteger(_tmp1, 12)  ;// Return Value: returns TEXTRANGE Structure (chrg & lpstrText)
;   } ;-----------------------------
;   Else If _action = EM_SETCHARFORMAT
;   {
; ;-- The EM_SETCHARFORMAT message sets character formatting in a rich edit control.
;     VarSetCapacity(_tmp1, 32, 0)     , InsertInteger(32, _tmp1, 16)
;     DllCall("SendMessage"                       ;// returns LRESULT in lResult
;                     , "UInt"        , _ctrlID   ;// handle to destination control
;                     , "UInt"        , 0x444     ;// message ID   (EM_SETCHARFORMAT := WM_USER + 68)
;                     , "UInt"        , 0         ;// wParam  - Character formatting that applies to the control
;                     , "Cdecl Str"  , &_tmp1)    ;// lParam  - Pointer to a CHARFORMAT structure specifying the character formatting to use
;     Return ExtractInteger(_tmp1, 0) . "|" . ExtractInteger(_tmp1, 4)  ;// Return Value: returns TEXTRANGE Structure (chrg & lpstrText)
;   } ;-----------------------------
;   Else If _action = EM_SETPAGEROTATE
;   {
; ;-- Deprecated. An application sends an EM_SETPAGEROTATE message to set the text layout for a Microsoft RE control
;     _tmp1 := DllCall("SendMessage"              ;// returns DWORD in lResult
;                     , "UInt"        , _ctrlID   ;// handle to destination control
;                     , "UInt"        , 0x45d     ;// message ID   (EM_SETPAGEROTATE := WM_USER + 93)
;                     , "UInt"        , 90        ;// wParam  - Text layout value (EPR_0, EPR_90, EPR_180, EPR_270)
;                     , "UInt"        , 0)        ;// lParam  - Not used; must be zero.
;     Return _tmp1  ;// Return Value:  the new text layout value
;   } ;-----------------------------
I attempted wrapping up a few more.. but now (being a dllcall noob) I'm hitting a brick wall when it comes to effectively using/understanding structures. I managed to get a simple one working.. but I'm not understanding how to create one w/ Int & Str's in it & ones where one of the members is another structure! (Ex. The TEXTRANGE Structure uses a CHARRANGE Structure as one of its members..)
You can strip off all the BS comments (I left them there to make it easier for someone more experienced to double check..) as well as come up w/ a better naming convention; I'm going to continue trying to improve my knowledge & will maybe post new ones as I get them figured out.

As you may have guessed, this function isn't designed only for use with a RICHEDIT
control.

This has quite a bit of potential.. & I'm axious to see how it develops :D

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
So preliminary remarks on corrupt's code:

Rich Edit version DLL
1.0 Riched32.dll
2.0 Riched20.dll - Nearly everywhere
3.0 Riched20.dll - in Windows Me, 2000 & XP
4.1 Msftedit.dll - In Windows XP SP1
[...]
To create a rich edit control, call the CreateWindowEx function, specifying the rich edit window class. If you are using Rich Edit 1.0 (Riched32.dll), specify RichEdit for the window class parameter. If you are using Rich Edit 2.0 or later (Riched20.dll), specify RICHEDIT_CLASS for the window class parameter.

I suggest to drop RichEdit 1.0 to go directly to 2.0/3.0, likely to have fewer bugs.

I see you have a _ClassName parameter, but you hard-coded it in the CreateWindowEx.
Also my use of GetWindowLong to get the hInstance is an error (The Old New Thing : What is the HINSTANCE passed to CreateWindow ...), it must be zero for controls residing in User32.dll, and should be the result of LoadLibrary otherwise, even if Windows seems to take the correct registered class if its name is unique.
It can be useful is some other program is running, using another DLL declaring the same class name (another version, another origin, etc.).
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

corrupt- This is really awesome! This has been a highly anticipated gui control.. & I'm looking forward to it being fully implemented.

Thanks and thanks for contributing :) . As there are a few structs that are commonly used when modifying RichEdit controls, I'll likely add a function to make using a few of the common ones a bit easier.

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

If you are using Rich Edit 2.0 or later (Riched20.dll), specify RICHEDIT_CLASS for the window class parameter.

I haven't been able to get this to work on my XP Pro system but I'll do a bit more research. I'm probably missing something simple...

I suggest to drop RichEdit 1.0 to go directly to 2.0/3.0, likely to have fewer bugs.

I'm not sure about the number of bugs but it might be worth it for the additional features.

I see you have a _ClassName parameter, but you hard-coded it in the CreateWindowEx.

Thanks. That was unintentionally left in from a test version.

Also my use of GetWindowLong to get the hInstance is an error (The Old New Thing : What is the HINSTANCE passed to CreateWindow ...), it must be zero for controls residing in User32.dll, and should be the result of LoadLibrary otherwise, even if Windows seems to take the correct registered class if its name is unique.
It can be useful is some other program is running, using another DLL declaring the same class name (another version, another origin, etc.).

Thanks. I wasn't aware of that.