- NSIS Discussion
- Installation page. Image in Listview
Archive: Installation page. Image in Listview
Pawel
25th August 2010 09:41 UTC
Installation page. Image in Listview
Hello,
I would like to know if it is possible to display an image onto NSIS default InstallPage (listview background).
(or even better, to change the background image of Listview control during installation, using few images).
I need something like this:
http://shup.com/Shup/406356/MUI_InstPage_Image.png
Have you got maybe knowledge how to display image (images) as a background of Listview control on install page? The goal is to display some images during installation (my installer is big, and it take some time to install all components - I thought, it would be nice to show user some images also (not only progressbar).
Ps: I know how to change header image during installation, i know how to display image on main window of installl page. I am asking directly about Listview control background.
-Pawel
Pawel
25th August 2010 17:12 UTC
Stu, thanks.
That may be useful. However, I have no idea how to use it using
NSIS script. My Win32 API knowledge is not as good as I want.
I tried:
FindWindow $R9 "#32770" "" $HWNDPARENT
GetDlgItem $R9 $R9 1016 ; 1016 is Listview control ID
I have handle to the control... And now what?
There is no definitioin in NSIS for "${LVM_SETBKIMAGE}".
Tried this:
SendMessage $R9${LVM_SETBKIMAGE} 0 "$PLUGINSDIR\Image1.bmp"
But it doeasnt work and I suppose it cant.
Any tips?
Ps: I found !define LVM_SETBKIMAGE "0x00001044"
Ps1: I found Delphi code to do what i want:
procedure TForm1
.DrawWallpaper;
var
lv : TLVBKIMAGE;
>begin
FillChar(lv, SizeOf(lv), 0);
lv.ulFlags := LVBKIF_SOURCE_URL or LVBKIF_STYLE_TILE;
lv.pszImage := 'c:\sample.bmp';
SendMessage(ListView1.Handle, LVM_SETBKIMAGE, 0, integer(@lv));
>end
>
http://www.kidmoses.com/blog-article.php?bid=16
There is even better code, to show image as Watermark. That would be better to me (the flag:
LVBKIF_TYPE_WATERMARK;).
procedure TForm1.DrawWallpaper;
var
lv : TLVBKIMAGE;
hinst: Thandle;
const
LVBKIF_TYPE_WATERMARK = $10000000;
>begin
hinst:= GetModuleHandle(nil);
if (
hinst = 0) then exit;
FillChar(lv, SizeOf(lv), 0);
lv.ulFlags := LVBKIF_TYPE_WATERMARK;
lv.hbm := LoadImage(hinst,PChar('LVWALLPAPER'),IMAGE_BITMAP,0,0,
>LR_CREATEDIBSECTION or
>LR_LOADTRANSPARENT);
SendMessage(ListView1.Handle, LVM_SETBKIMAGE, 0, integer(@lv));
>end;
Can someone translate it to NSIS using system plugin?
In my code, i use incorrectly last param of sendmessage, as it is a pointer to structure (and I just put there image path)
-Pawel
Animaether
25th August 2010 18:46 UTC
The WaterMark variant is quite different and relies on the hBITMAP variety which is undocumented (claimed as unsupported, but just has several issues with it).
Just setting a regular/tiled background image is easier. You can use the below as a header if you like..
!include "LogicLib.nsh"
!ifndef LVBKIF_SOURCE_URL
!define LVBKIF_SOURCE_URL 0x00000002
!endif
!ifndef LVBKIF_STYLE_NORMAL
!define LVBKIF_STYLE_NORMAL 0x00000000
!endif
!ifndef LVBKIF_STYLE_TILE
!define LVBKIF_STYLE_TILE 0x00000010
!endif
!ifndef LVBKIF_FLAG_TILEOFFSET
!define LVBKIF_FLAG_TILEOFFSET 0x000000100
!endif
!ifndef LVM_FIRST
!define LVM_FIRST 0x1000
!endif
!ifndef LVM_SETBKIMAGE
!ifdef NSIS_UNICODE
!define /math LVM_SETBKIMAGE ${LVM_FIRST} + 138
!else
!define /math LVM_SETBKIMAGE ${LVM_FIRST} + 68
!endif
!endif
!macro SetListViewBkgImage ListView ImageFile Tile xPos yPos
Push $2 /* Stack: $2 */
Push $0 /* Stack: $0 $2 */
StrCpy $0 ${ListView}
Push $1 /* Stack: $1 $0 $2 */
StrCpy $1 ${LVBKIF_SOURCE_URL}
${If} ${Tile} == 1
IntOp $1 $1 | ${LVBKIF_STYLE_TILE}
IntOp $1 $1 | ${LVBKIF_FLAG_TILEOFFSET}
${Else}
IntOp $1 $1 | ${LVBKIF_STYLE_NORMAL}
${EndIf}
System::Call "*(ir1, i0, t'${ImageFile}', i0, i${xPos}, i${yPos}) i.r1"
SendMessage $0 ${LVM_SETBKIMAGE} 0 $1 $2
Pop $1 /* Stack: $0 $2 */
Pop $0 /* Stack: $2 */
Exch $2 /* Stack: result of SendMessage */
!macroend
!define SetListViewBkgImage `!insertmacro SetListViewBkgImage`
( Note: For Unicode NSIS, a different define of LVM_SETBKIMAGE is required. Comment/uncomment the defines as necessary. )
The parameters should be fairly self-explanatory... supply it the hwnd to a ListView element, the path or URL to an image (I think it's BMP, ICO, GIF and JPG that are supported), whether (1) or not (0) you want to tile the image, and it's X and Y position as percentages if not tiled or pixels if tiled (Requires XPStyle on to work properly).
Which you can then use as follows:
XPStyle on
Section"FirstSection"
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $0 $0 1016
${SetListViewBkgImage} $0 "c:\test.jpg" 0 100 100
SectionEnd
>
The background will scroll with the content, though, which I guess is why you were pondering the watermark one.
Animaether
25th August 2010 18:50 UTC
You can go over this page to figure out the watermarked variant - it's fairly step-by-step as they went through the same issue :)
http://tortoisesvn.net/listcontrol_watermark
Pawel
25th August 2010 19:07 UTC
Thanks for the code.
However i am testing this now and it seems to not working...
Trying to find out why.
What does this means:
System::Call "*(ir1, i0, t'${ImageFile}', i0, i${xPos}, i${yPos}) i.r1"
I mean, what we call here?
Animaether
25th August 2010 19:30 UTC
Originally posted by Pawel
What does this means:
System::Call "*(ir1, i0, t'${ImageFile}', i0, i${xPos}, i${yPos}) i.r1"
I mean, what we call here?
Nothing is called there - a new Struct is created from scratch. That Struct includes the flags, the image file name and the position information (the other two parameters are not used). The pointer to the struct is then stored in $1. That pointer is then used in the SendMessage command.
See also the System plugin documentation (in your NSIS folder, not the online version!).
Pawel
25th August 2010 19:32 UTC
Ok, thanks for explanation. Now that make sense for me.
Still trying to find why it doeasnt work.
-Pawel
Animaether
25th August 2010 19:36 UTC
No idea... here's a complete test script using your forum 'avatar' as the image:
http://www.pastebin.ca/1925263
And the result image (gone in 7 days):
http://picpaste.com/temp-mHRGQsui.png
Pawel
25th August 2010 19:41 UTC
Yes, indeed, your example works very well...
Maybe it is because I use MUI2?
Afrow UK
25th August 2010 19:44 UTC
This might be your answer:
Because the list-view control uses OLE COM to manipulate the background images, the calling application must call CoInitialize or OleInitialize before sending this message. It is best to call one of these functions when the application is initialized and call either CoUninitialize or OleUninitialize when the application is terminating.
Stu
Animaether
25th August 2010 19:54 UTC
Originally posted by Afrow UK
This might be your answer
(forum quote fail)
Possibly... I did have it (Ole variant) added at first (in .noGUIInit and .onGUIEnd), but the commands were always non-zero, even though the result worked. I checked around and kichik mentions back in December 2006:
Originally posted by kichik
NSIS already calls OleInitialize for plug-ins, which internally calls CoInitializeEx.
So I thought maybe it just wasn't needed, and left it out %)
But I guess he might mean it uses OleInitialize for the call then OleUninitialize right after. If it's still needed for the SendMessage, then that could explain it, possibly, maybe. *eyes the witchcraft*
Pawel
25th August 2010 20:06 UTC
Hmm, or maybe this is becasue Unicode compiler...
I copied code to example Modern UI (WelcomeFinish.nsi) script, and I can see image, when I compile it in NSIS (ansi).
Btw, is this ok:
System::Call "Ole32.dll::CoInitializeEx()"
-Pawel
Pawel
25th August 2010 20:27 UTC
OK, I found that when I compile it under NSIS Unicode this line:
System::Call "*(ir1, i0, t'${ImageFile}', i0, i${xPos}, i${yPos}) i.r1"
should be changed to this:
System
::Call "*(ir1, i0, m'${ImageFile}', i0, i${xPos}, i${yPos}) i.r1"
System plugin help says:
m ANSI text, string. (FYI: 'm' for multibyte string or 'w' flipped over.)
t text, string (pointer to first character). Like TCHAR*, it is a Unicode string in Unicode NSIS.
Hmm, it is strange. I thought "t" should be in Unicode, not ansi...
-Pawel
Animaether
25th August 2010 20:28 UTC
It does indeed seem to fail in the Unicode version here as well. Can't say I can spot immediately why.
Edit: Ah, you already found it. Odd indeed.. I guess 't = Unicode in Unicode NSIS' isn't a gaurantee that it works %)
Edit 2: But I guess it makes sense if the internals behind LVM_SETBKIMAGE specifically expect an ANSI string.
Edit 3: It does indeed. There's a separate message code for Unicode. It's $LVM_FIRST + 138, rather than ${LVM_FIRST} + 68 for ANSI. If you use that, then you can leave the 't' type in place.
Animaether
25th August 2010 20:37 UTC
I've adjusted the original script post to point out the unicode define change. I'm not sure if there's a compiler-side way of finding out which variant of NSIS is being used to make the switch automatically.
Edit: !ifdef ${NSIS_UNICODE}
Pawel
25th August 2010 20:42 UTC
Yeah, you are right. 138 works.
But, there is a problem yet, that the image is not still at the bottom of control (when log gathering text durring installation).
And, is there any possibility to refresh the control?
(I want to display there lets say 10 images. Each image per section). So, if I display 1 image in 1 section, it should be destroyed and 2 image should be displayed.
Ps: Thanks for help guys. Really appreciated.
Animaether
25th August 2010 20:50 UTC
I think you can only set 1 image (short of the old image not being cleared when you set another image). The issue of the image scrolling with the content.. well, that's the watermark style.. see earlier post.. the code so far should give you something to work with that at least works :)
Pawel
25th August 2010 21:24 UTC
To make it as Watermark you mean it should use something like this (as written in above post)
procedure TForm1
.DrawWallpaper;
var
lv : TLVBKIMAGE;
hinst: Thandle;
const
LVBKIF_TYPE_WATERMARK = $10000000;
>begin
hinst:= GetModuleHandle(nil);
if (hinst = 0) then exit;
FillChar(lv, SizeOf(lv), 0);
lv.ulFlags := LVBKIF_TYPE_WATERMARK;
lv.hbm := LoadImage(hinst,PChar('LVWALLPAPER'),IMAGE_BITMAP,0,0,
>LR_CREATEDIBSECTION or
>LR_LOADTRANSPARENT);
SendMessage(ListView1.Handle, LVM_SETBKIMAGE, 0, integer(@lv));
>end;
I Defined it:
!ifndef LVBKIF_TYPE_WATERMARK
!define LVBKIF_TYPE_WATERMARK 0x10000000
!endif
and then changed:
${If} ${Tile} == 1
IntOp $1 $1 | ${LVBKIF_STYLE_TILE}
IntOp $1 $1 | ${LVBKIF_FLAG_TILEOFFSET}
${Else}
IntOp $1 $1 | ${LVBKIF_TYPE_WATERMARK}
${EndIf}
But, there must be done something else, to make it work... In above code there is LoadImage()... Any idea? I mean solution? (tried few configurations, none works).
As I said, I want to display the image that will be in the same place, even if lsitview will scroll text, adding new lines.
-Pawel
Ps: Or maybe I will turn on Tile (set it to 1) and create image with dimensions as big as listview width/height... then, it should be displayed once...
Afrow UK
25th August 2010 22:28 UTC
As far as I understand reading the MSDN page you need to specify LVBKIF_STYLE_NORMAL to set coordinates when not using LVBKIF_FLAG_TILEOFFSET | LVBKIF_STYLE_TILE.
Edit: (as well as LVBKIF_TYPE_WATERMARK)
Stu
Pawel
25th August 2010 22:52 UTC
This doeasn't work:
${If} ${Tile} == 1
IntOp$1 $1 | ${LVBKIF_STYLE_TILE}
IntOp $1 $1 | ${LVBKIF_FLAG_TILEOFFSET}
${Else}
IntOp $1 $1 | ${LVBKIF_TYPE_WATERMARK}
IntOp $1 $1 | ${LVBKIF_STYLE_NORMAL}
${EndIf}
-Pawel
Animaether
26th August 2010 01:21 UTC
Originally posted by Pawel
To make it as Watermark you mean it should use something like this (as written in above post)
Something like that... I highly recommend following this page as it goes over each step...
http://tortoisesvn.net/listcontrol_watermark
You'll still need to switch that over to NSIS/System plugin usage, but with the code you already have that should be doable.
Pawel
26th August 2010 09:07 UTC
Yes, Thanks.
The main problem is translating it to NSIS/System plugin code. As this is rather complicated.
Will try. What can I do :D
-Pawel
Afrow UK
26th August 2010 09:45 UTC
I might have to put that into a plug-in. It'd be a nice feature.
Stu
Pawel
26th August 2010 09:58 UTC
Would be much easier to use.
Plugin that shows an image (png/jpg/bmp/ico) on InstPage in Listview control. With few settings (Tiled/Normal/Watermark).
And user can call it in 1 section. Plugin will display proper image in proper position with proper settings (tile/normal/watermark). If user call it once again, in another section (2 section, 3 section) it should destroy previous image, refresh control and load new one...
If that is possible, would be great plugin.
The goal is to display many images durring installation. User then can see what is just installed (or something).
-Pawel
Animaether
26th August 2010 13:14 UTC
bah, where's your sense of adventure ;)
Ease of use would be about the same - but I suspect it'd be less error-prone, at least :)
Pawel
26th August 2010 14:16 UTC
Yeah. I tried to translate it... but, hmm... to complicated.
You really must now what you do using System plugin.
And plugin would be better. So, I hope Stu will code it.
-Pawel
Afrow UK
26th August 2010 16:49 UTC
Sorry I don't want to keep your hopes up because I am very busy at the moment.
Stu
Pawel
26th August 2010 17:27 UTC
No problem. I am sure if you find time you will make it :P