- NSIS Discussion
- RichEdit with nsDialogs?
Archive: RichEdit with nsDialogs?
vbguy
8th March 2008 01:04 UTC
RichEdit with nsDialogs?
I can't seem to figure out how to add a RichEdit control with nsDialogs. I've tried
nsDialogs::CreateControl /NOUNLOAD RICHEDIT_CLASS ${WS_VISIBLE}|${WS_CHILD}|${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_WANTRETURN} ${__NSD_Text_EXSTYLE} 0 10u 100% 90u ''
>Pop $txtLicense
>
But nothing shows. I tried looking at the MUI2 header files, but those are no cakewalk to read. I also glanced at the nsDialogs source files, but that lead me no closer to an answer.
Anders
8th March 2008 08:01 UTC
RICHEDIT_CLASS is not the real name, try "RICHEDIT" or "RichEdit20A"
vbguy
8th March 2008 08:18 UTC
Thanks, RichEdit20A worked.
bradharding
11th March 2008 08:15 UTC
Hi!
Now that I know how to create a richedit control using nsDialogs - thanks for that - how would I go about loading an .rtf file into the control using the system plugin?
Thanks in advance.
Brad.
bradharding
11th March 2008 11:27 UTC
Thanks for your response, Anders.
The thread you've linked to though has example code that, as best as I can tell, only streams text into the control. Is there any [easy] way to load an actual Rich Text File into the control? I've tried googling for a Windows API command that can do it, but with no luck...
Anders
11th March 2008 11:37 UTC
its the same message (http://msdn2.microsoft.com/en-us/lib...02(VS.85).aspx )
just use SF_RTF (2)
and easy? there is no such thing when the system plugin is involved ;)
bradharding
11th March 2008 11:45 UTC
Thanks again! I'll give it a go.... :D
vbguy
11th March 2008 12:51 UTC
I just use this to load the RTF data. The article states '.txt' files, but RTF are just text files with funky formatting.
http://nsis.sourceforge.net/External_License_file
Also, if you want a thin border on the RichEdit box, just use the WS_EX_STATICEDGE flag instead of __NSD_Text_EXSTYLE.
Here's the full code I use:
)
>nsDialogs::CreateControl /NOUNLOAD "RichEdit20A" ${WS_VISIBLE}|${WS_CHILD}|${WS_CLIPSIBLINGS}|${WS_TABSTOP}|${ES_READONLY}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_WANTRETURN} ${WS_EX_STATICEDGE} 0 10u 100% 90u ''
>Pop $txtLicense
>;load the license from file
>!insertmacro addLicense
>
bradharding
11th March 2008 22:36 UTC
Thanks for your help, vbguy! Here's my code:
nsDialogs::CreateControl /NOUNLOAD "RichEdit20A" ${WS_VISIBLE}|${WS_CHILD}|\
${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|\
${ES_WANTRETURN} ${WS_EX_STATICEDGE} 0 15u 100% 80u ""
Pop $CONTROL
File "/oname=$PLUGINSDIR\Readme.rtf" "Resources\Readme.rtf"
System::Call "kernel32::CreateFile(t '$PLUGINSDIR\Readme.rtf', i ${GENERIC_READ}, \
i ${FILE_SHARE_READ}, i 0, i ${OPEN_EXISTING}, i 0, i 0) i .r0"
System::Call "kernel32::GetFileSize(i r0, i 0) i .r1"
MessageBox MB_OK "$1"
IntOp $1 $1 + 1
System::Alloc $1
Pop $2
System::Call "kernel32::ReadFile(i r0, i r2, i r1, *i .r3, i 0)"
System::Call "kernel32::CloseHandle(i r0)"
SendMessage $CONTROL ${WM_SETTEXT} $CONTROL $2
System::Free $2
Unfortunately, it doesn't display all of the RTF file. I'm guessing this is because of the value that GetFileSize returns, and the additional bytes used for the formatting of the RTF file. I tried using EM_STREAMIN as Anders suggested, but I'm completely lost with that bit of code.... Any further ideas?
Brad.
vbguy
11th March 2008 22:52 UTC
You need to replace the hwnd to point to your added text box (or rich text box):
textbox handle
>Var LicFile ;pointer to the license file in memory
>!macro addLicense
${If} $LicFile == ""
ClearErrors
System
::Call 'kernel32::CreateFile(t "$PLUGINSDIR\\EULA.rtf", i 0x80000000, i 1, i 0, i 3, i 0, i 0) i .r0'
IfErrors exit
;allocate memory for the file
System::Call 'kernel32::GetFileSize(i r0, i 0) i .r1'
System::Alloc $1
Pop$2
;read the file into memory
System::Call 'kernel32::ReadFile(i r0, i r2, i r1, *i .r3, i 0)'
System::Call 'kernel32::CloseHandle(i r0)'
Push $2 ;Push the License memory location to stack
Pop $LicFile;Pop the License memory loc to the variable LicFile
${EndIf}
;White background, black text
SetCtlColors $txtLicense 0x000000 0x00FFFFFF
SendMessage $txtLicense${WM_SETTEXT} 0 $LicFile
exit:
!macroend
>
Then, deallocate the memory when the installer closes:
onGUIEnd
System
::Free $LicFile ;clear the memory allocated for the license file
FunctionEnd
>
Anders
11th March 2008 23:01 UTC
vbguy: you can't use WM_SETTEXT, it seems to work for some simple rtf files, but not everything
bradharding
11th March 2008 23:11 UTC
Thanks for your help guys.
Anders, any chance you could give us some example code that streams an .rtf file into a richtext control using EM_STREAMIN? Please? Before I lose any more hair? :eek:
Anders
11th March 2008 23:39 UTC
no, can't make it work, I end up with the same problem as WM_SETTEXT, the file will not load 100%
vbguy
11th March 2008 23:41 UTC
Originally posted by Anders
vbguy: you can't use WM_SETTEXT, it seems to work for some simple rtf files, but not everything
You're right, it would likely fail if embedded images. I'd also like to see an example using streaming (if you have time).
Edit: Where in the MUI2 code is the RTF data loaded for the standard license page? Is it done using a C/C++ plugin, or is the file loaded using NSIS code?
Couldn't we just hijack the default code and apply it to the newly created RichEdit box?
bradharding
12th March 2008 00:20 UTC
Fixed! It's always something simple that's causing the problem isn't it?
All you have to do is use EM_EXLIMITTEXT to set the size of the rich text control!
; Show the License.
nsDialogs::CreateControl /NOUNLOAD "RichEdit20A" ${WS_VISIBLE}|${WS_CHILD}|${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|\
${ES_WANTRETURN} ${WS_EX_STATICEDGE} 0 15u 100% 80u ""
Pop $CONTROL
File "/oname=$PLUGINSDIR\${GNU_GENERAL_PUBLIC_LICENSE}.rtf" "Resources\${GNU_GENERAL_PUBLIC_LICENSE}.rtf"
System::Call "kernel32::CreateFile(t '$PLUGINSDIR\${GNU_GENERAL_PUBLIC_LICENSE}.rtf', i ${GENERIC_READ}, \
i ${FILE_SHARE_READ}, i 0, i ${OPEN_EXISTING}, i 0, i 0) i .r0"
System::Call "kernel32::GetFileSize(i r0, i 0) i .r1"
IntOp $1 $1 + 1
System::Alloc $1
Pop $2
System::Call "kernel32::ReadFile(i r0, i r2, i r1, *i .r3, i 0)"
System::Call "kernel32::CloseHandle(i r0)"
SendMessage $CONTROL ${EM_EXLIMITTEXT} 0 $1
SendMessage $CONTROL ${WM_SETTEXT} 0 $2
System::Free $2
Anders
12th March 2008 00:55 UTC
that changes nothing, thats only the length, even a rtf file less than 300bytes will not load, atleast not for me
bradharding
12th March 2008 00:59 UTC
That's strange. The RTF file I'm using is about 58Kb. Without sending that EM_EXLIMITTEXT message, it wouldn't display the last quarter of the file. With the message, it displays it fine.
vbguy
12th March 2008 01:02 UTC
Originally posted by Anders
that changes nothing, thats only the length, even a rtf file less than 300bytes will not load, atleast not for me
I just tested your file with the WM_SETTEXT method, and it works for me (even without EM_EXLIMITTEXT message).
Thanks, bradharding, for finding that flag.
Anders
12th March 2008 01:30 UTC
alright, I think this works
!define SF_RTF 2
!define EM_STREAMIN 1097
!define EM_EXLIMITTEXT 1077
Function lic
FileOpen $4 "test.rtf" r
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $0 $0 1000
SendMessage $0 ${EM_EXLIMITTEXT} 0 0x7fffffff
System::Get /NoUnload "(i, i .R0, i .R1, i .R2) iss"
Pop $2
System::Call /NoUnload "*(i 0, i 0, k r2) i .r3"
System::Call /NoUnload "user32::SendMessage(i r0, i ${EM_STREAMIN}, i ${SF_RTF}, i r3) i.s"
loop:
Pop $0
StrCmp $0 "callback1" 0 done
System::Call /NoUnload "kernel32::ReadFile(i $4, i $R0, i $R1, i $R2, i 0)"
Push 0 # callback's return value
System::Call /NoUnload "$2"
goto loop
done:
System::Free $2
System::Free $3
FileClose $4
FunctionEnd
the code is a little bit longer than the WM_SETTEXT code, but it does not alloc the whole file at once, and does not know or care about the filesize at all
vbguy
12th March 2008 02:13 UTC
Originally posted by Anders
alright, I think this works
the code is a little bit longer than the WM_SETTEXT code, but it does not alloc the whole file at once, and does not know or care about the filesize at all
Sweet. Now that all this is known, maybe someone can submit it for inclusion into the nsDialogs.nsh header.
kichik
16th March 2008 20:37 UTC
You should.
vbguy
17th March 2008 20:10 UTC
Originally posted by kichik
You should.
I meant to include it as a built-in part of the nsDialogs. I could include it with my own nsDialogs.nsh file, but I'd just overwrite it the next time I updated to a newer official version.
Also, I think the community would benefit by having all the RichEdit particulars figured out in the nsDialogs.nsh.
I didn't mean to belittle your work.
If you were implying that I should do the work, then I would be glad to. I have just a couple of questions, though:
1. On what version of nsDialogs.nsh do I base my work? The latest svn version, or the latest "stable" version?
2. In what form do I post the results? A patch file or the full file?
3. Where do I post the results? Here, or is there another NSIS developer forum?
kichik
17th March 2008 20:19 UTC
I misread it as "maybe I should submit it for inclusion" and wanted to encourage you to do so by letting you know I will include it in the next version.
If you wish to make the change and submit it, you should base your work on the latest nsDialogs.nsh from Subversion. But even if you don't, this is not a fundamental change, so a patch for 2.35's version won't be hard to apply.
The best way is to submit a patch to patch tracker, but a full file would work as well.
grundic
12th May 2009 12:45 UTC
I made a test file:
nsh
>!include LogicLib.nsh
OutFile test
.exe
>Var Dialog
>Var txtLicense
Page custom nsDialog
>Function nsDialog
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
nsDialogs::CreateControl /NOUNLOAD RichEdit20A ${WS_VISIBLE}|${WS_CHILD}|${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_WANTRETURN} ${__NSD_Text_EXSTYLE} 0 10u 100% 90u ''
Pop $txtLicense
call lic
nsDialogs::Show
FunctionEnd
>!define SF_RTF 2
>!define EM_STREAMIN 1097
>;!define EM_EXLIMITTEXT 1077
>Function lic
MessageBox MB_OK "Inside function"
FileOpen $4 "test.rtf" r
FindWindow$0 "#32770" "" $HWNDPARENT
GetDlgItem$0 $0 1000
SendMessage$0 ${EM_EXLIMITTEXT} 0 0x7fffffff
System::Get /NoUnload "(i, i .R0, i .R1, i .R2) iss"
Pop $2
System::Call /NoUnload "*(i 0, i 0, k r2) i .r3"
System::Call /NoUnload "user32::SendMessage(i r0, i ${EM_STREAMIN}, i ${SF_RTF}, i r3) i.s"
loop:
Pop $0
StrCmp$0 "callback1" 0 done
System::Call /NoUnload "kernel32::ReadFile(i $4, i $R0, i $R1, i $R2, i 0)"
Push 0 # callback's return value
System::Call /NoUnload "$2"
goto loop
done:
System::Free $2
System::Free $3
FileClose$4
FunctionEnd
Section
SectionEnd
>
But nothing was shown in RichEdit. What am I doing wrong?
Also, anybody say how can I show custom text in RichEdit without using RTF file?
Thanks in advance!
grundic
22nd May 2009 11:36 UTC
Okay, I find a way to display RTF with aforementioned code. But now I got another problem: if I go back to page with custom RTF, then nothing is shown is RichEdit. I've found, that
Pop $0 ; <--- HERE!
MessageBox MB_OK $0
StrCmp$0 "callback1" 0 done
System
::Call /NoUnload "kernel32::ReadFile(i $4, i $R0, i $R1, i $R2, i 0)"
Push 0 # callback's return value
System::Call /NoUnload "$2"
goto loop
>
Pop $0 returns 0 :cry:
Can anybody help me to solve this issue?
grundic
23rd May 2009 09:01 UTC
I've found an other way to solve my problem
...
...
...
>Page custom CreatePageFinish
>...
...
...
Function CreatePageFinish
nsDialogs
::Create /NOUNLOAD 1018
Pop $DIALOG
nsDialogs::CreateControl /NOUNLOAD RichEdit20A ${WS_VISIBLE}|${WS_CHILD}|${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_WANTRETURN}|${ES_READONLY} ${__NSD_Text_EXSTYLE} 0 0 100% 140 ''
Pop $RICHEDIT
CustomLicense::LoadFile "License.rtf" $RICHEDIT
;Call LoadLicense
### Radiobuton: auto install ###
${NSD_CreateRadioButton} 1 180 450 15 "Qick insall"
Pop $RADIO_AUTO
${NSD_CreateRadioButton} 1 210 450 15 "Custom install"
Pop $RADIO_MANUAL
nsDialogs::Show
FunctionEnd
>
Hope, it'll help someone.
Here is link to the plugin:
http://nsis.sourceforge.net/CustomLicense_plug-in ;)
Animaether
23rd May 2009 21:50 UTC
for what it's worth - I think the actual problem is that callback1 rolls over to callback2 the next time your page is generated.
I'm not sure if there's an appropriate method to prevent that, but you can always make the "callback1" check more dynamic by Pop'ing it into a var first, then Push'ing it back onto the stack, and then compare against that var.
Here's a function + macro + define (I'll post to wiki after review from smarter people):
/* Check if LoadRTFincluded is defined so that LoadRTF can't be included twice */
!ifndef LoadRTFincluded
/*
Defines used by LoadRTF
*/
!ifndef SF_RTF
!define SF_RTF 2
!endif
!ifndef EM_STREAMIN
!define EM_STREAMIN 1097
!endif
!ifndef EM_EXLIMITTEXT
!define EM_EXLIMITTEXT 1077
!endif
/*
LoadRTF Main Function
Inserted via macro for Installer vs Uninstaller
Loads an RTF file into a RichEdit20a control
Thanks to Anders for all the heavy work
Define/Macro/Functionfication and a CallbackN tweak by Animaether
Usage:
Push Path-to-RTF-file
Push HWND-of-RichEdit20A-control
Call LoadRTF
-or for uninstallers-
Call un.LoadRTF
*/
!macro LoadRTFfunc Un
Function ${Un}LoadRTF
; RichEdit20A.hwnd RTFilePath.string
Exch $0 ; $0 RTFilePath.string ; $0 = RichEdit20A.hwnd
Exch ; RTFilePath.string $0
Exch $1 ; $1 $0 ; $1 = RTFilePath.string
Push $2 ; $2 $1 $0 ; $2 : Callback address
Push $3 ; $3 $2 $1 $0 ; $3 : EDITSTREAM structure
Push $4 ; $4 $3 $2 $1 $0 ; $4 : callbackN identifier from System plugin to check against
Push $R0 ; $R0 $4 $3 $2 $1 $0 ; $R0 : Buffer that receives data from ReadFile()
Push $R1 ; $R1 $R0 $4 $3 $@ $1 $0 ; $R1 : Maximum number of bytes to read using ReadFile()
Push $R2 ; $R2 $R1 $R0 $4 $3 $@ $1 $0 ; $R2 : Number of bytes actually read using ReadFile()
SendMessage $0 ${EM_EXLIMITTEXT} 0 0x7fffffff
System::Get "(i, i .R0, i .R1, i .R2) iss"
Pop $2
System::Call "*(i 0, i 0, k r2) i .r3"
System::Call "user32::SendMessage(i r0, i ${EM_STREAMIN}, i ${SF_RTF}, i r3) i.s"
Pop $4
Push $4
ClearErrors
FileOpen $1 "$1" r ; $1 = RTFile.handle
_loop:
Pop $0 ; $0 = callbackN identifier from System plugin.
StrCmp $0 "$4" 0 _done ; check if System plugin's callbackN matches the stored callbackN
System::Call "kernel32::ReadFile(i $1, i $R0, i $R1, i $R2, i 0)"
Push 0 # callback's return value
System::Call "$2"
goto _loop
_done:
FileClose $1
System::Free $3
System::Free $2
; $R2 $R1 $R0 $4 $3 $2 $1 $0
Pop $R2 ; $R1 $R0 $4 $3 $2 $1 $0
Pop $R1 ; $R0 $4 $3 $2 $1 $0
Pop $R0 ; $4 $3 $2 $1 $0
Pop $4 ; $3 $2 $1 $0
Pop $3 ; $2 $1 $0
Pop $2 ; $1 $0
Pop $1 ; $0
Pop $0 ; -empty-
FunctionEnd
!macroend
!insertmacro LoadRTFfunc ""
!insertmacro LoadRTFfunc "un."
/*
LoadRTF Ease of use functions.
Simplifies usage of LoadRTF.
Usage:
${LoadRTF} "Path-to-RTF-file" HWND-of-RichEdit20A-control
-or for uninstallers-
${un.LoadRTF} "Path-to-RTF-file" HWND-of-RichEdit20A-control
*/
!macro LoadRTF file hwnd
Push "${file}"
Push ${hwnd}
Call LoadRTF
!macroend
!define LoadRTF `!insertmacro LoadRTF`
!macro un.LoadRTF file hwnd
Push "${file}"
Push ${hwnd}
Call un.LoadRTF
!macroend
!define un.LoadRTF `!insertmacro LoadRTF.un`
/* define LoadRTFincluded so that LoadRTF can't be included twice */
!define LoadRTFincluded
!endif
Usage is pretty simple. Presuming you have the above saved in "LoadRTF.nsh":
!addplugindir "."
!addincludedir "."
!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
!include "MUI2.nsh"
!include "LoadRTF.nsh"
OutFile "test.exe"
var dialog
var hwnd
var null
Page custom null ; just for testing
Page custom test
Page custom null ; just for testing
Function .onInit
InitPluginsDir
FunctionEnd
var RichEditControl
Function test
nsDialogs::Create 1018
Pop $dialog
nsDialogs::CreateControl "RichEdit20A" \
"${DEFAULT_STYLES}|${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_READONLY}" \
"${WS_EX_WINDOWEDGE}|${WS_EX_CLIENTEDGE}" \
0 0 100% 100% ""
Pop $RichEditControl
SetOutPath "$PLUGINSDIR"
File "helloworld.rtf"
${LoadRTF} "$PLUGINSDIR\helloworld.rtf" $RichEditControl
nsDialogs::Show
FunctionEnd
Function null
nsDialogs::Create 1018
Pop $dialog
${NSD_CreateLabel} 0 0 100% 8% "Dummy page."
Pop $null
nsDialogs::Show
FunctionEnd
Section
SectionEnd
*crosses fingers*
grundic
25th May 2009 10:53 UTC
I've found more easy way (see my next post) to fix probkem with RTF.
But thanks anyway :)
Animaether
25th May 2009 12:14 UTC
I know - it's just there for completeness sake and for those who can't, or don't want to, rely on plugins :)
chetanphanse
1st July 2013 16:32 UTC
Show Rich text in control
I want to show some rich text in a Label that does not come from any file. Soemthing like
This is bold and this is in italics and this is underlined.
The text gets generated dynamically at run time, so i cant use multiple controls. Kindly suggest how can I achieve this
thanks for your help