Useful InstallOptions and MUI macros

From NSIS Wiki
Jump to navigationJump to search
Author: bholliger (talk, contrib)


Description

I created some macros that are useful for some InstallOptions tasks. Feel free to use and/or add your own macros as well. I hope they are useful.

General tips and hints for InstallOptions

Due to the fact that the field numbers define the tab-order this might change after adding another element to the dialog. Therefore I recommend using constants that hold the field names in a single location. This improves the general readability of the code as well.

;----------------------
;defines for Dialog 1 - sample.ini
 
  !define DIALOG1 "sample.ini"
 
  !define CHK_BLUE "Field 2"
  !define CHK_RED "Field 3"
  !define CHK_GREEN "Field 4"
  !define CHK_BROWN "Field 5"
  !define CHK_PROXYSETTINGS "Field 6"
  !define GRP_PROXYOPTIONS "Field 7"
  !define LBL_IPADDRESS "Field 8"
  !define TXT_IPADDRESS "Field 9"
  !define LBL_PORT1 "Field 10"
  !define TXT_PORT1 "Field 11"
  !define CHK_ENCRYPTION "Field 12"
  !define DRQ_NSISPATH "Field 14"
  !define CMD_AUTODETECTNSISPATH "Field 15"

Macro: GROUPCONTROLS

Activate a group of controls according to another control like a checkbox.

; Activate a group of controls, depending on the state of one control
; 
; Usage:
; 
; eg. !insertmacro GROUPCONTROLS "${DIALOG1}" "${CHK_PROXYSETTINGS}" 
; "${LBL_IPADDRESS}|${TXT_IPADDRESS}|${LBL_PORT1}|${TXT_PORT1}|${CHK_ENCRYPTION}"
; FILE:          INI-file in $pluginsdir
; SOURCECONTROL: RadioButton, Checkbox
; CONTROLGROUP:  pipe delimited list of controls; ${BUTTON1}|${CHECKBOX}|${TEXTFIELD}
;
; Requires:
;
; !include "WordFunc.nsh"
; !insertmacro WordReplace
; !insertmacro WordFind
;
!macro GROUPCONTROLS FILE SOURCECONTROL CONTROLGROUP
  Push $R0 ;holds element
  Push $R1 ;counter
  Push $R2 ;state of the control
  Push $R3 ;flags of the control / hwnd of the control
 
  !insertmacro MUI_INSTALLOPTIONS_READ $R2 "${FILE}" "${SOURCECONTROL}" "State"
 
  StrCpy $R1 1
  ${Do}
    ClearErrors
    ${WordFind} "${CONTROLGROUP}" "|" "E+$R1" $R0
 
    ${If} ${Errors}
    ${OrIf} $R0 == ""
      ${ExitDo}
    ${EndIf}
 
    ; Put state change in flags of element as well
    !insertmacro MUI_INSTALLOPTIONS_READ $R3 "${FILE}" "$R0" "Flags"
    ${If} "$R2" == "1"
      ${WordReplace} $R3 "DISABLED" "" "+" $R3
       ${WordReplace} $R3 "||" "|" "+" $R3
      !insertmacro MUI_INSTALLOPTIONS_WRITE "${FILE}" "$R0" "Flags" $R3
    ${Else}
      !insertmacro MUI_INSTALLOPTIONS_WRITE "${FILE}" "$R0" "Flags" "$R3|DISABLED"
    ${EndIf}
 
    !insertmacro MUI_INSTALLOPTIONS_READ $R3 "${FILE}" "$R0" "HWND"
    EnableWindow $R3 $R2
 
    IntOp $R1 $R1 + 1
  ${Loop}
 
  Pop $R3
  Pop $R2
  Pop $R1
  Pop $R0
 
!macroend

Macro: CHANGETEXTFIELD

Change the content of a textfield and write it to ini file. Ideal for autodetect purposes.

;change text field and put value in ini file
; 
; Usage:
;
;  !insertmacro CHANGETEXTFIELD "${DIALOG1}" "${DRQ_NSISPATH}" $tmp
;
; FILE:    INI-file in $pluginsdir
; ELEMENT: name of the control
; VALUE:   value that should appear in control
;
!macro CHANGETEXTFIELD FILE ELEMENT VALUE
  Push $R0 ; holds value
  !insertmacro MUI_INSTALLOPTIONS_WRITE ${VALUE} "${FILE}" "${ELEMENT}" "State"
  !insertmacro MUI_INSTALLOPTIONS_READ $R0 "${FILE}" "${ELEMENT}" "HWND"
  SendMessage $R0 ${WM_SETTEXT} 0 "STR:${VALUE}"
  Pop $R0
!macroend

Macro: CHECKBOXCHECKER

Counts the number of activated controls and displays an error message if there are not enough controls selected. Select 3 or 4 out of 6.

; checks a group of checkboxes and counts how many of them
; are activated.
;
; Usage:
;
; Create a langstring containing an error message
; eg. LangString TEXT_LIMITATIONSEXEEDED ${LANG_ENGLISH} "Choose either two or three colours!"
;
; eg. !insertmacro CHECKBOXCHECKER "${DIALOG1}" "${CHK_BLUE}|${CHK_RED}|${CHK_GREEN}|${CHK_BROWN}" 2 3
;
; FILE:          INI-file in $pluginsdir
; CONTROLGROUP:  pipe delimited list of controls; ${BUTTON1}|${CHECKBOX}|${TEXTFIELD}
; MIN/MAX:       at least ${MIN} and no more than ${MAX} controls must be in activated state
;
; Requires:
;
; !include "WordFunc.nsh"
; !insertmacro WordFind
;
!macro CHECKBOXCHECKER FILE CONTROLGROUP MIN MAX
 
  Push $R0 ;holds element
  Push $R1 ;counter
  Push $R2 ;count activated elements
  Push $R3 ;state of the control
 
  StrCpy $R1 1
  StrCpy $R2 0
  ${Do}
    ClearErrors
    ${WordFind} "${CONTROLGROUP}" "|" "E+$R1" $R0
 
    ${If} ${Errors}
    ${OrIf} $R0 == ""
      ${ExitDo}
    ${EndIf}
 
    ; Put state change in flags of element as well
    !insertmacro MUI_INSTALLOPTIONS_READ $R3 "${FILE}" "$R0" "State"
    ${If} "$R3" == "1"
      IntOp $R2 $R2 + 1
    ${EndIf}
 
    IntOp $R1 $R1 + 1
  ${Loop}
 
  ${If} $R2 < ${MIN}
  ${OrIf} $R2 > ${MAX}
    MessageBox MB_OK|MB_ICONSTOP "$(TEXT_LIMITATIONSEXCEEDED)"
    Abort
  ${EndIf}
 
  Pop $R3
  Pop $R2
  Pop $R1
  Pop $R0
 
!macroend

Sample

Have a look at the sample.

;---------------------
;Include Modern UI
 
  !include "MUI.nsh"
  !include "LogicLib.nsh"
;----------------------
;defines for Dialog 1 - sample.ini
 
  !define DIALOG1 "sample.ini"
 
  !define CHK_BLUE "Field 2"
  !define CHK_RED "Field 3"
  !define CHK_GREEN "Field 4"
  !define CHK_BROWN "Field 5"
  !define CHK_PROXYSETTINGS "Field 6"
  !define GRP_PROXYOPTIONS "Field 7"
  !define LBL_IPADDRESS "Field 8"
  !define TXT_IPADDRESS "Field 9"
  !define LBL_PORT1 "Field 10"
  !define TXT_PORT1 "Field 11"
  !define CHK_ENCRYPTION "Field 12"
  !define DRQ_NSISPATH "Field 14"
  !define CMD_AUTODETECTNSISPATH "Field 15"
;--------------------------------
;General
 
  ;Name and file
  Name "MUI installation validation example"
  OutFile "test.exe"
 
  ;Default installation folder
  InstallDir "$PROGRAMFILES\MUI validate"
 
  ;Get installation folder from registry if available
  InstallDirRegKey HKCU "Software\MUI validate" ""
 
;--------------------------------
;Pages
 
  Page custom InitPage ValidatePage
  !insertmacro MUI_PAGE_DIRECTORY
  !insertmacro MUI_PAGE_INSTFILES
 
;--------------------------------
;Interface Settings
 
  !define MUI_ABORTWARNING
 
;--------------------------------
;Languages
 
  !insertmacro MUI_LANGUAGE "English"
 
;--------------------------------
;Reserve Files
 
  ;If you are using solid compression, files that are required before
  ;the actual installation should be stored first in the data block,
  ;because this will make your installer start faster.
 
  ReserveFile "sample.ini"
  !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
 
;--------------------------------
;Variables
 
  Var "tmp"
 
!include "WordFunc.nsh"
!insertmacro WordReplace
!insertmacro WordFind
 
; Activate a group of controls, depending on the state of one control
; 
; Usage:
; 
; eg. !insertmacro GROUPCONTROLS "${DIALOG1}" "${CHK_PROXYSETTINGS}" "${LBL_IPADDRESS}|${TXT_IPADDRESS}|${LBL_PORT1}|${TXT_PORT1}|${CHK_ENCRYPTION}"
; FILE:          INI-file in $pluginsdir
; SOURCECONTROL: RadioButton, Checkbox
; CONTROLGROUP:  pipe delimited list of controls; ${BUTTON1}|${CHECKBOX}|${TEXTFIELD}
;
; Requires:
;
; !include "WordFunc.nsh"
; !insertmacro WordReplace
; !insertmacro WordFind
;
!macro GROUPCONTROLS FILE SOURCECONTROL CONTROLGROUP
  Push $R0 ;holds element
  Push $R1 ;counter
  Push $R2 ;state of the control
  Push $R3 ;flags of the control / hwnd of the control
 
  !insertmacro MUI_INSTALLOPTIONS_READ $R2 "${FILE}" "${SOURCECONTROL}" "State"
 
  StrCpy $R1 1
  ${Do}
    ClearErrors
    ${WordFind} "${CONTROLGROUP}" "|" "E+$R1" $R0
 
    ${If} ${Errors}
    ${OrIf} $R0 == ""
      ${ExitDo}
    ${EndIf}
 
    ; Put state change in flags of element as well
    !insertmacro MUI_INSTALLOPTIONS_READ $R3 "${FILE}" "$R0" "Flags"
    ${If} "$R2" == "1"
      ${WordReplace} $R3 "DISABLED" "" "+" $R3
       ${WordReplace} $R3 "||" "|" "+" $R3
      !insertmacro MUI_INSTALLOPTIONS_WRITE "${FILE}" "$R0" "Flags" $R3
    ${Else}
      !insertmacro MUI_INSTALLOPTIONS_WRITE "${FILE}" "$R0" "Flags" "$R3|DISABLED"
    ${EndIf}
 
    !insertmacro MUI_INSTALLOPTIONS_READ $R3 "${FILE}" "$R0" "HWND"
    EnableWindow $R3 $R2
 
    IntOp $R1 $R1 + 1
  ${Loop}
 
  Pop $R3
  Pop $R2
  Pop $R1
  Pop $R0
 
!macroend
 
;change text field and put value in ini file
; 
; Usage:
;
;  !insertmacro CHANGETEXTFIELD "${DIALOG1}" "${DRQ_NSISPATH}" $tmp
;
; FILE:    INI-file in $pluginsdir
; ELEMENT: name of the control
; VALUE:   value that should appear in control
;
!macro CHANGETEXTFIELD FILE ELEMENT VALUE
  Push $R0 ; holds value
  !insertmacro MUI_INSTALLOPTIONS_WRITE ${VALUE} "${FILE}" "${ELEMENT}" "State"
  !insertmacro MUI_INSTALLOPTIONS_READ $R0 "${FILE}" "${ELEMENT}" "HWND"
  SendMessage $R0 ${WM_SETTEXT} 0 "STR:${VALUE}"
  Pop $R0
!macroend
 
; checks a group of checkboxes and counts how many of them
; are activated.
;
; Usage:
;
; Create a langstring containing an error message
; eg. LangString TEXT_LIMITATIONSEXEEDED ${LANG_ENGLISH} "Choose either two or three colours!"
;
; eg. !insertmacro CHECKBOXCHECKER "${DIALOG1}" "${CHK_BLUE}|${CHK_RED}|${CHK_GREEN}|${CHK_BROWN}" 2 3
;
; FILE:          INI-file in $pluginsdir
; CONTROLGROUP:  pipe delimited list of controls; ${BUTTON1}|${CHECKBOX}|${TEXTFIELD}
; MIN/MAX:       at least ${MIN} and no more than ${MAX} controls must be in activated state
;
; Requires:
;
; !include "WordFunc.nsh"
; !insertmacro WordFind
;
!macro CHECKBOXCHECKER FILE CONTROLGROUP MIN MAX
 
  Push $R0 ;holds element
  Push $R1 ;counter
  Push $R2 ;count activated elements
  Push $R3 ;state of the control
 
  StrCpy $R1 1
  StrCpy $R2 0
  ${Do}
    ClearErrors
    ${WordFind} "${CONTROLGROUP}" "|" "E+$R1" $R0
 
    ${If} ${Errors}
    ${OrIf} $R0 == ""
      ${ExitDo}
    ${EndIf}
 
    ; Put state change in flags of element as well
    !insertmacro MUI_INSTALLOPTIONS_READ $R3 "${FILE}" "$R0" "State"
    ${If} "$R3" == "1"
      IntOp $R2 $R2 + 1
    ${EndIf}
 
    IntOp $R1 $R1 + 1
  ${Loop}
 
  ${If} $R2 < ${MIN}
  ${OrIf} $R2 > ${MAX}
    MessageBox MB_OK|MB_ICONSTOP "$(TEXT_LIMITATIONSEXCEEDED)"
    Abort
  ${EndIf}
 
  Pop $R3
  Pop $R2
  Pop $R1
  Pop $R0
 
!macroend
 
;--------------------------------
;Installer Sections
 
Section "Dummy Section" SecDummy
 
  SetOutPath "$INSTDIR"
 
  ;ADD YOUR OWN FILES HERE...
 
SectionEnd
 
;--------------------------------
;Installer Functions
 
Function .onInit
 
;  !insertmacro MUI_LANGDLL_DISPLAY
 
FunctionEnd
 
LangString TEXT_IO_TITLE ${LANG_ENGLISH} "Test page"
LangString TEXT_IO_SUBTITLE ${LANG_ENGLISH} "Test some things"
LangString TEXT_LIMITATIONSEXCEEDED ${LANG_ENGLISH} "Choose either two or three colours!"
 
Function InitPage
 
  ;Extract InstallOptions INI files
  !insertmacro MUI_INSTALLOPTIONS_EXTRACT "${DIALOG1}"
 
  !insertmacro MUI_HEADER_TEXT "$(TEXT_IO_TITLE)" "$(TEXT_IO_SUBTITLE)"
  !insertmacro MUI_INSTALLOPTIONS_INITDIALOG "${DIALOG1}"
  !insertmacro GROUPCONTROLS "${DIALOG1}" "${CHK_PROXYSETTINGS}" "${LBL_IPADDRESS}|${TXT_IPADDRESS}|${LBL_PORT1}|${TXT_PORT1}|${CHK_ENCRYPTION}"
  !insertmacro MUI_INSTALLOPTIONS_SHOW  
 
FunctionEnd
 
Function ValidatePage
  ; handle notify event of element
  !insertmacro MUI_INSTALLOPTIONS_READ $tmp "${DIALOG1}" "Settings" "State"  
  ${Switch} "Field $tmp"
    ${Case} "${CHK_PROXYSETTINGS}"
      !insertmacro GROUPCONTROLS "${DIALOG1}" "${CHK_PROXYSETTINGS}" "${LBL_IPADDRESS}|${TXT_IPADDRESS}|${LBL_PORT1}|${TXT_PORT1}|${CHK_ENCRYPTION}"
      Abort
    ${Case} "${CMD_AUTODETECTNSISPATH}"
      ;read registry value and set text field
      ReadRegStr $tmp HKLM "Software\NSIS" ""
      !insertmacro CHANGETEXTFIELD "${DIALOG1}" "${DRQ_NSISPATH}" $tmp
      Abort      
  ${EndSwitch}
 
  ; check if 2 or 3 check boxes are activated
  !insertmacro CHECKBOXCHECKER "${DIALOG1}" "${CHK_BLUE}|${CHK_RED}|${CHK_GREEN}|${CHK_BROWN}" 2 3
 
  ;  check if checkbox is activated
  !insertmacro MUI_INSTALLOPTIONS_READ $tmp "${DIALOG1}" "${CHK_PROXYSETTINGS}" "State"  
  ${If} $tmp == 1
    Var /GLOBAL ip1
    Var /GLOBAL port1
 
    !insertmacro MUI_INSTALLOPTIONS_READ $ip1 "${DIALOG1}" "${TXT_IPADDRESS}" "State"
    Push "$ip1"
    Call ValidateIP
    ${If} ${Errors}
      MessageBox MB_ICONEXCLAMATION "IP not correct! $ip1"
      Abort
    ${EndIf}
 
    !insertmacro MUI_INSTALLOPTIONS_READ $port1 "${DIALOG1}" "${TXT_PORT1}" "State"
    ${If} $port1 < 1024
    ${OrIf} $port1 > 32768
      MessageBox MB_ICONEXCLAMATION "Port not valid!"
      Abort
    ${EndIf}
  ${EndIf}
 
  ; check for file in NSIS' path
  !insertmacro MUI_INSTALLOPTIONS_READ $tmp "${DIALOG1}" "${DRQ_NSISPATH}" "State"
  ${Unless} ${FileExists} "$tmp\makensis.exe"
    MessageBox MB_OK|MB_ICONEXCLAMATION "makensis.exe in NSIS path not found!"
    Abort
  ${EndIf}
 
  ; TODO: save values to ini file/registry...
 
FunctionEnd
 
; http://nsis.sourceforge.net/Validate_IP_function
!include WordFunc.nsh
 
!insertmacro WordFind
!insertmacro StrFilter
Function ValidateIP
 
  Exch $0
  Push $1
  Push $2
 
  ${StrFilter} $0 1 "." "" $1
  ${If} $0 != $1
    # invalid charcaters used
    #   example: a127.0.0.1
    Goto error
  ${EndIf}
 
  ${WordFind} $0 . "#" $1
  ${If} $1 != 4
    # wrong number of numbers
    #   example: 127.0.0.
    Goto error
  ${EndIf}
 
  ${WordFind} $0 . "*" $1
  ${If} $1 != 3
    # wrong number of dots
    #   example: 127.0.0.1.
    Goto error
  ${EndIf}
 
  ${For} $2 1 4
    ${WordFind} $0 . +$2 $1
 
    ${If} $1 > 255
    ${OrIf} $1 < 0
      # invalid number
      #   example: 500.0.0.1
      Goto error
    ${EndIf}
  ${Next}
 
  Pop $2
  Pop $1
  Pop $0
 
  ClearErrors
 
  Return
 
  error:
 
    Pop $2
    Pop $1
    Pop $0
 
    SetErrors
 
FunctionEnd

sample.ini

; Ini file generated by the HM NIS Edit IO designer.
[Settings]
NumFields=15
 
[Field 1]
Type=Groupbox
Text=Colour (choose 2 or 3)
Left=0
Right=112
Top=0
Bottom=56
 
[Field 2]
Type=CheckBox
Text=&Blue
Left=5
Right=80
Top=10
Bottom=20
State=0
 
[Field 3]
Type=CheckBox
Text=&Red
Left=5
Right=80
Top=20
Bottom=30
 
[Field 4]
Type=CheckBox
Text=&Green
Left=5
Right=80
Top=30
Bottom=40
 
[Field 5]
Type=CheckBox
Text=&Brown
Left=5
Right=79
Top=40
Bottom=49
 
[Field 6]
Type=Checkbox
Text=&Set proxy options (NOTIFY)
Flags=NOTIFY
Left=117
Right=225
Top=0
Bottom=10
 
[Field 7]
Type=Groupbox
Left=117
Right=-1
Top=10
Bottom=56
Text=Proxy options
 
[Field 8]
Type=Label
Text=&IP-Address:
Left=122
Right=165
Top=22
Bottom=30
 
[Field 9]
Type=Text
Left=122
Right=240
Top=30
Bottom=42
 
[Field 10]
Type=Label
Text=&Port:
Left=244
Right=264
Top=22
Bottom=30
 
[Field 11]
Type=Text
Left=244
Right=-10
Top=30
Bottom=42
 
[Field 12]
Type=Checkbox
Text=&Use encryption
Left=122
Right=187
Top=44
Bottom=54
 
[Field 13]
Type=Groupbox
Text=Set NSIS path or click autodetect
Left=117
Right=-1
Top=61
Bottom=116
 
[Field 14]
Type=DirRequest
Left=122
Right=-10
Top=77
Bottom=89
 
[Field 15]
Type=Button
Text=&Autodetect
Flags=NOTIFY
Left=245
Right=-10
Top=92
Bottom=106