mojo
mojo

Reputation: 4132

NSIS: How do I allow multiple user choice screens that select the sections to install?

I'm attempting to create an installer that asks the user a series of questions to decide which components to install. Each choice (potentially) influences the available options in later choices (else I would just do a normal components page—I don't want to give the user invalid options).

How do I accomplish such a thing? If I just use the components page, all the options are shown, some combinations of which are completely invalid. I don't want to let the user select those. Is it possible to leave out the components page?

Here's a minimal working example of what I'm trying. (Sorry it's long, I couldn't really simplify the dialog code.)

!include nsDialogs.nsh
!include Sections.nsh
Name "mwe"
OutFile "mwe.exe"
InstallDir C:\mwe

Var hwnd
Var Level1Opt

Page custom SelectLevel1Opt ProcessLevel1

Function SelectLevel1Opt
   nsDialogs::Create 1018
   pop $hwnd

   ${NSD_CreateLabel} 0 0 100% 12u "Please select level 1 option"
   Pop $hwnd

   ${NSD_CreateRadioButton} 10% 12u 100% 12u "Level 1 A"
   Pop $hwnd
   nsDialogs::SetUserData $hwnd "Level 1 A"
   ${NSD_OnClick} $hwnd SetLevel1

   ${NSD_CreateRadioButton} 10% 24u 100% 12u "Level 1 B"
   Pop $hwnd
   nsDialogs::SetUserData $hwnd "Level 1 B"
   ${NSD_OnClick} $hwnd SetLevel1

   nsDialogs::Show
FunctionEnd

Function SetLevel1
   Pop $hwnd
   nsDialogs::GetUserData $hwnd
   Pop $Level1Opt
   MessageBox MB_OK "Selected: $Level1Opt"
FunctionEnd

Function ProcessLevel1
   ${If} $Level1Opt == "Level 1 A"
      !insertmacro SelectSection Level1A
   ${ElseIf} $Level1Opt == "Level 1 B"
      !insertmacro SelectSection Level1B
   ${EndIf}
FunctionEnd


Page directory
Page instfiles

Section ""
  MessageBox MB_OK "Common Install"
SectionEnd

Section /o "" Level1A
  MessageBox MB_OK "Level 1 A"
SectionEnd

Section /o "" Level1B
  MessageBox MB_OK "Level 1 B"
SectionEnd

No matter what I choose, neither Level1A nor Level1B sections get run. The selection from the dialog is correctly detected in the handler and the post function. However, selecting the sections isn't causing them to run. Even if I add a components page, neither of them is selected.

I looked in Selection.nsh, and the example it refers to (one-section.nsi) doesn't really do what I want, because it uses the components page. (I also don't understand quite how it works.)

What am I doing wrong? In what way am I misunderstanding the way NSIS is supposed to work?

Upvotes: 0

Views: 1377

Answers (1)

Anders
Anders

Reputation: 101666

As idleberg says, the correct syntax is !insertmacro SelectSection ${Level1A} but you get a warning because the section id is not defined until after its Section instruction in your .nsi. You need to move the functions that use ${Level1A} below the sections in your source code:

!include nsDialogs.nsh
!include Sections.nsh
Page custom SelectLevel1Opt ProcessLevel1
Page instfiles

Section ""
  MessageBox MB_OK "Common Install"
SectionEnd

Section /o "a" Level1A
  MessageBox MB_OK "Level 1 A"
SectionEnd

Section /o "b" Level1B
  MessageBox MB_OK "Level 1 B"
SectionEnd

Var hInnerDialog
Var hL1A
Var hL1B

Function SelectLevel1Opt
nsDialogs::Create 1018
pop $hInnerDialog
${NSD_CreateLabel} 0 0 100% 12u "Please select level 1 option"
Pop $0
${NSD_CreateRadioButton} 10% 12u 100% 12u "Level 1 A"
Pop $hL1A
nsDialogs::SetUserData $hL1A ${Level1A} ; Only used by the generic function
${NSD_CreateRadioButton} 10% 24u 100% 12u "Level 1 B"
Pop $hL1B
nsDialogs::SetUserData $hL1B ${Level1B} ; Only used by the generic function
nsDialogs::Show
FunctionEnd

Function ProcessLevel1
${NSD_GetState} $hL1A $1
${If} $1 <> ${BST_UNCHECKED}
    !insertmacro SelectSection ${Level1A}
    !insertmacro UnselectSection ${Level1B}
${Else}
    !insertmacro SelectSection ${Level1B}
    !insertmacro UnselectSection ${Level1A}
${EndIf}
FunctionEnd

The ProcessLevel1 function can also be implemented as a loop if there are many radio buttons:

Function ProcessLevel1
StrCpy $0 ""
loop:
    FindWindow $0 "${__NSD_RadioButton_CLASS}" "" $hInnerDialog $0 
    System::Call "USER32::GetWindowLong(p$0,i${GWL_STYLE})i.r1"
    IntOp $1 $1 & ${BS_AUTORADIOBUTTON}
    ${If} $1 = ${BS_AUTORADIOBUTTON} ; Is it a auto radio button?
        nsDialogs::GetUserData $0 ; Get the section id
        Pop $2
        ${NSD_GetState} $0 $1
        ${If} $1 <> ${BST_UNCHECKED}
            !insertmacro SelectSection $2
        ${Else}
            !insertmacro UnselectSection $2
        ${EndIf}
    ${EndIf}
    IntCmp $0 0 "" loop loop
FunctionEnd

Upvotes: 1

Related Questions