I can make a function to work for all tools? - nsis

I want to have something like this :
o Simulink
o Simulink 3D Animation
o Simulink Control Design
o Stateflow
o Stateflow Coder
and for example when "o Simulink 3D Animation" is checked, Simulink should automatically be checked and the same thing for Stateflow.
I have the following code for Section :
Section /o "Simulink"
;SectionIn RO
FileWrite $9 "product=Simulink$\r$\n"
AddSize 0
SectionEnd
Section "Simulink 3D Animation"
SectionIn RO
FileWrite $9 "product=Simulink 3D Animation$\r$\n"
AddSize 0
SectionEnd
Section /o "Simulink Control Design"
;SectionIn RO
FileWrite $9 "product=Simulink Control Design$\r$\n"
AddSize 0
SectionEnd
Section "Stateflow"
SectionIn RO
FileWrite $9 "product=Stateflow$\r$\n"
AddSize 0
SectionEnd
Section "Stateflow Coder"
SectionIn RO
FileWrite $9 "product=Stateflow Coder$\r$\n"
AddSize 0
SectionEnd
And for checked I have the following function :
Section /o "Simulink 3D Animation" box_BONUS
;SectionIn RO
FileWrite $9 "product=Simulink 3D Animation$\r$\n"
AddSize 0
SectionEnd
Section /o "Simulink" box_MAIN
;SectionIn RO
FileWrite $9 "product=Simulink$\r$\n"
AddSize 0
SectionEnd
Function .OnSelChange
${If} ${SectionIsSelected} ${box_BONUS}
MessageBox MB_OK "simple message box"
!insertmacro SelectSection ${box_MAIN}
!insertmacro SetSectionFlag ${box_MAIN} ${SF_RO}
${Else}
!insertmacro ClearSectionFlag ${box_MAIN} ${SF_RO}
${EndIf}
FunctionEnd
With this code, I don't have the tools grouped, and I don't want to have a function for each tool : one for Simulink, one for Stateflow.... Can I make a function to work for all tools?

You can use SectionGroup like this:
SectionGroup "Simulink"
Section "Simulink 3D Animation"
# your code
SectionEnd
Section "Simulink Control Design"
# your code
SectionEnd
SectionGroupEnd
I don't know exactly how your installer is supposed to work. But if, for instance, any sub-section requires some mandatory files, you could add a required section inside your SectionGroup.
SectionGroup "Simulink"
Section -"Required Simulink Stuff"
# your code
SectionEnd
# more Sections
SectionGroupEnd
This will always run, once any section inside that SectionGroup has been selected (notice the dash in front of the section name!)
If you're looking for a more advanced setup, why not simply call a function from one of your sub-sections?
SectionGroup "Simulink"
Section "Simulink 3D Animation"
Call Requirements_3dAnim
SectionEnd
# more Sections
SectionGroupEnd
Function Requirements_3dAnim
# your code
Functions

Related

NSIS Installer button skinning not working in custom page

I'm creating a custom page for my installer which I have the default buttons (back, next and cancel) hidden and only a custom button (install) is shown.
Problem is that, I'm not able to set the background color of the custom button using skinned controls plugin.
If I show the default buttons, I see the background color applied to them via the bitmap.
This means the skinning is getting applied for the default buttons, but not on the custom button.
Any pointers on what I would be missing ?
Here is the code:
!include "logiclib.nsh"
;--------------------------------
;Include Modern UI
!include "MUI2.nsh"
!include "InstallOptions.nsh"
;--------------------------------
;General
;Name and file
Name "Custom App"
OutFile "custom_installer.exe"
;Default installation folder
InstallDir "$LOCALAPPDATA\Custom Test"
RequestExecutionLevel user
;Remove default branding text of Nullsoft
;BrandingText " "
var title
var description
var button
Var ImageCtrl
Var BmpHandle
;--------------------------------
;Interface Settings
!define MUI_ABORTWARNING
!define Black "814EFA"
!define LightRed "FFFFFF"
!define MUI_CUSTOMFUNCTION_GUIINIT myGUIInit
XPStyle off
;--------------------------------
;Functions
Function .onInit
InitPluginsDir
; Extract bitmaps for buttons
File "/oname=$PLUGINSDIR\button.bmp" "${NSISDIR}\Contrib\SkinnedControls\skins\button_background.bmp"
FunctionEnd
Function myGUIInit
DetailPrint "myGUIInit"
; start the plugin
; the /disabledtextcolor, /selectedtextcolor and /textcolor parameters are optionnal
SkinnedControls::skinit /NOUNLOAD \
/disabledtextcolor=808080 \
/selectedtextcolor=000080 \
/textcolor=000000 \
"/button=$PLUGINSDIR\button.bmp"
FunctionEnd
Function Start
nsDialogs::Create 1044
Pop $0
SetCtlColors $0 222425 FBFBFB
SetCtlColors $HWNDPARENT 222425 FBFBFB
${NSD_CreateLabel} 0 150 100% 24u "Welcome to Custom App"
pop $title
SetCtlColors $title 0x000000 0xFBFBFB
${NSD_AddStyle} $title ${SS_CENTER}
CreateFont $0 "Arial" 24
SendMessage $title ${WM_SETFONT} $0 1
${NSD_CreateLabel} 0 190 100% 28u "By clicking install you agree to the terms of the End $\r$\n User License Agreement"
pop $description
SetCtlColors $description 0x000000 0xFBFBFB
${NSD_AddStyle} $description ${SS_CENTER}
CreateFont $0 "Arial" 14
SendMessage $description ${WM_SETFONT} $0 1
${NSD_CreateButton} 25 250 90% 15u INSTALL
pop $button
;SetCtlColors $button 0x000000 0x0FFFFF
${NSD_AddStyle} $button ${SS_CENTER}
GetDlgItem $0 $HWNDPARENT 3 ; Back Button
GetDlgItem $1 $HWNDPARENT 1 ; Next/Close Button
GetDlgItem $2 $HWNDPARENT 2 ; Cancel Button
;ShowWindow $0 ${SW_HIDE}
;ShowWindow $1 ${SW_HIDE}
;ShowWindow $2 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 1035
ShowWindow $0 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 1036
ShowWindow $0 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 1045
ShowWindow $0 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 1028
ShowWindow $0 ${SW_HIDE}
GetDlgItem $0 $HWNDPARENT 1256
ShowWindow $0 ${SW_HIDE}
nsDialogs::Show
${NSD_FreeBitmap} $BmpHandle
FunctionEnd
;--------------------------------
;Pages
Page custom Start
!insertmacro MUI_PAGE_COMPONENTS
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English"
;--------------------------------
;Installer Sections
Section "Dummy Section" SecDummy
SetOutPath "$INSTDIR"
SectionEnd
And here is the current UI (which clearly shows default buttons skinned):
This is a bug in Skinned Control plug-in you use, you need to fix it.
There is a chance some older version had this issue fixed, but your script is not working for me with older version for unknown reasons and I do not remember the details/versions exactly.
I am developer of Graphical Installer (http://www.graphical-installer.com/) mentioned in the Wiki page of that plug-in and I worked on the SkinnedControls plugin some time ago and fixed some issues. But that was almost 10 years ago.
As far as I remember there were some issues with this plug-in because
we wanted to use it in Graphical Installer (tool that allows to skin the whole installer).
However we could not solve all issues so we ended writing custom plugin where the buttons are working fine:
When I have time I will look at this plug-in, meanwhile you can open the project (it is Visual Studio 2008 C++ project I suppose) and debug it. The plugin should enumerate all controls, detect all Buttons (this is probably a problem) and skin them.

NSIS section sizes doubled due to use of SetOverwrite

I've inherited an installer script, and I'm annoyed by it claiming to require twice as much space as it actually needs.
I found that this was due to how each section is using SetOverwrite (because they're re-used for a repair install?). I understand that it's necessary to keep the duplicated File commands in each of the If/Else blocks because of how SetOverwrite works (see below), but I have confirmed that it results in a doubling of the automatic section size calculations.
${MementoSection} $(APP_Section_SecHelp) SecHelp
SetDetailsPrint textonly
DetailPrint $(APP_DetailPrint_SecHelp)
SetDetailsPrint listonly
SetOutPath $INSTDIR
SectionIn 1 2
${If} $SetOverwriteOn == TRUE
SetOverwrite ifnewer
File /r "${APP_SOURCE_DIR}\Help"
${Else}
SetOverwrite off
File /r "${APP_SOURCE_DIR}\Help"
${EndIf}
SectionGetFlags ${SecHelp} $SecHelp_GetFlag
${MementoSectionEnd}
Is this a bad design pattern that I should change? Do I need to add a hack to call SectionGetSize, divide by 2 and call SectionSetSize?
Personally I would just use SetOverwrite ifnewer for both but if you absolutely want to do it your way then using SectionSetSize is one option.
Another thing you could do is to put the repair File /r instruction in a separate function that you call from the section. The downside of a function is that the progressbar does not move much during the extraction.
A third alternative is to put some of the repair tasks in a separate section that is unchecked by default and you enable it when you are in repair mode.
Edit: Here is a example that uses SectionSetSize:
!include LogicLib.nsh
InstallDir $Temp
Page Components InitComponentsPage
Page Directory
Page InstFiles
!macro ModifySectionHack SID TEMPVAR
SectionGetSize ${SID} ${TEMPVAR}
IntOp ${TEMPVAR} ${TEMPVAR} / 2
SectionSetSize ${SID} ${TEMPVAR}
!macroend
Function InitComponentsPage
StrCpy $0 0
loop:
ClearErrors
SectionGetFlags $0 $1 ; The error flag is set if we try to access a section that does not exist
IfErrors done
!insertmacro ModifySectionHack $0 $1
IntOp $0 $0 + 1
Goto loop
done:
FunctionEnd
Section "Foo"
InitPluginsDir ; Need a place to extract to for this example
SetOutPath $PluginsDir
${If} 1 <> 2 ; Don't really care about the result
SetOverwrite ifnewer
File "${NSISDIR}\bin\makensis.exe" ; ~400kb
${Else}
SetOverwrite off
File "${NSISDIR}\bin\makensis.exe"
${EndIf}
SectionEnd
Section "Bar"
InitPluginsDir ; Need a place to extract to for this example
SetOutPath $PluginsDir
${If} 1 <> 2 ; Don't really care about the result
SetOverwrite ifnewer
File "${NSISDIR}\nsis.exe" ; ~700kb
${Else}
SetOverwrite off
File "${NSISDIR}\nsis.exe"
${EndIf}
SectionEnd
This loops through all the sections and adjusts all of them but I'm not sure if that is such a good idea. If this was my script then I would manually call ModifySectionHack on each section that has the SetOverwrite problem:
!include LogicLib.nsh
InstallDir $Temp
Page Components
Page Directory
Page InstFiles
!macro ModifySectionHack SID TEMPVAR
SectionGetSize ${SID} ${TEMPVAR}
IntOp ${TEMPVAR} ${TEMPVAR} / 2
SectionSetSize ${SID} ${TEMPVAR}
!macroend
Section "Foo" SID_FOO
InitPluginsDir ; Need a place to extract to for this example
SetOutPath $PluginsDir
${If} 1 <> 2 ; Don't really care about the result
SetOverwrite ifnewer
File "${NSISDIR}\bin\makensis.exe" ; ~400kb
${Else}
SetOverwrite off
File "${NSISDIR}\bin\makensis.exe"
${EndIf}
SectionEnd
Section "Bar"
${If} 1 = 2
File /r "${NSISDIR}\stubs"
${EndIf}
SectionEnd
Section "Baz" SID_BAZ
InitPluginsDir ; Need a place to extract to for this example
SetOutPath $PluginsDir
${If} 1 <> 2 ; Don't really care about the result
SetOverwrite ifnewer
File "${NSISDIR}\nsis.exe" ; ~700kb
${Else}
SetOverwrite off
File "${NSISDIR}\nsis.exe"
${EndIf}
SectionEnd
Function .onInit
; Adjust the section size for the sections that use SetOverwrite:
!insertmacro ModifySectionHack ${SID_FOO} $1
!insertmacro ModifySectionHack ${SID_BAZ} $1
FunctionEnd

NSIS unattended options

I want to build a NSIS-script, which has three section
section Main
section Minor
section Shared
Shared is invisible and would be installed, if Main or Minor is checked. If I start the installer, every section (Main, Minor) is checked.
Now it should be able to define the section (in silent install). What have I to change, to only install Main or Minor or Both?
Name "Test"
Outfile "Test.exe"
;RequestExecutionLevel ?
!include "Sections.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh" ;For GetOptions
Page Components "" "" EnforceSectionDependencies
Page InstFiles
Section /o "Main" SID_MAIN
DetailPrint Main
SectionEnd
Section /o "Minor" SID_MINOR
DetailPrint Minor
SectionEnd
Section "" SID_SHARED
DetailPrint Shared
SectionEnd
!macro CheckSectionSwitch sw sid
${GetOptions} $0 '${sw}' $9
${IfNot} ${Errors}
StrCpy $1 1
!insertmacro SelectSection ${sid}
${EndIf}
!macroend
Function .onInit
${GetParameters} $0
StrCpy $1 0 ;Any section swithes?
ClearErrors
!insertmacro CheckSectionSwitch '/Main' ${SID_MAIN}
!insertmacro CheckSectionSwitch '/Minor' ${SID_MINOR}
${If} $1 = 0
;Set defaults
!insertmacro SelectSection ${SID_MAIN}
!insertmacro SelectSection ${SID_MINOR}
${EndIf}
call EnforceSectionDependencies
FunctionEnd
Function EnforceSectionDependencies
!insertmacro UnselectSection ${SID_SHARED}
${If} ${SectionIsSelected} ${SID_MAIN}
${OrIf} ${SectionIsSelected} ${SID_MINOR}
!insertmacro SelectSection ${SID_SHARED}
${EndIf}
FunctionEnd
You should look at the Section management part of the documentation, notably the SectionSetFlags to change the sections selections.
Also, maybe that the How to control Section selections, while using SubSections & InstTypes example will be useful.

How do I only disable a section in a certain InstType, but force it on otherwise (even in the "Custom" InstType)?

I have a Section that is set to RO and occurs in all InstTypes except for a special one.
When the user first selects the special InstType and then selects "Custom" as the InstType the section is unchecked and can't be checked again in the "Custom" InstType.
So everything is fine except that when the users selects the "Custom" InstType the Section should always be checked again.
Only in the special InstType use case it should be unchecked.
How can I achieve this?
You can enforce whatever logic you want in .onSelChange:
!include WinMessages.nsh
!include LogicLib.nsh
!include Sections.nsh
Page Components
Page InstFiles
InstType "Normal1"
!define CIT_Special 1
InstType "Special"
InstType "Normal2"
Section Foo
SectionIn 1 2 3
SectionEnd
Section Bar SEC_Special
SectionIn 1 3
SectionIn RO
SectionEnd
Section Baz
SectionIn 1 2 ;3 << Not the same as Foo just to have some sort of difference
SectionEnd
Function .onSelChange
;Normally you would call GetCurInstType here, but it seems we need a little hack to detect the custom section
FindWindow $1 "#32770" "" $HWNDPARENT
GetDlgItem $1 $1 0x3F9
SendMessage $1 ${CB_GETCURSEL} 0 0 $2
SendMessage $1 ${CB_GETITEMDATA} $2 0 $2
${If} $2 = ${CIT_Special}
!insertmacro UnselectSection ${SEC_Special}
${Else}
!insertmacro SelectSection ${SEC_Special}
${EndIf}
FunctionEnd

How do I make a section required for a sectiongroup in NSIS?

I want to make a section inside of a sectiongroup required for that section group but not required for the entire install if the group is unchecked. I've tried "SectionIn R0" but this makes that section required for the entire install and I only want it to be required if they select the group itself.
SectionGroup "group"
Section "required for section group"
SectionIn RO
SectionEnd
Section "optional"
SectionEnd
SectionGroupEnd
You can't do this with just properties, you need some actual code:
!include Sections.nsh
!include LogicLib.nsh
SectionGroup /e "group"
Section "required for section group" SEC_REQ
SectionIn RO
SectionEnd
Section "optional" SEC_OPT
SectionEnd
SectionGroupEnd
Function .onSelChange
${If} ${SectionIsSelected} ${SEC_OPT}
!define /math MYSECTIONFLAGS ${SF_SELECTED} | ${SF_RO}
!insertmacro SetSectionFlag ${SEC_REQ} ${MYSECTIONFLAGS}
!undef MYSECTIONFLAGS
${Else}
!insertmacro ClearSectionFlag ${SEC_REQ} ${SF_RO}
${EndIf}
FunctionEnd
Note that there is a "bug" when unchecking the section group itself, to workaround that, you need to keep some global state since nsis does not tell you which section generated the notification. The following code has better logic:
!include Sections.nsh
!include LogicLib.nsh
SectionGroup /e "group" SEC_GRP
Section "required for section group" SEC_REQ
SectionIn RO
SectionEnd
Section "optional" SEC_OPT
SectionEnd
Section "" PRIVSEC_TOGGLESTATE ;hidden section to keep track of state
SectionEnd
SectionGroupEnd
Function .onSelChange
!define /math SECFLAGS_SELRO ${SF_SELECTED} | ${SF_RO}
${IfNot} ${SectionIsSelected} ${PRIVSEC_TOGGLESTATE}
${AndIf} ${SectionIsReadOnly} ${SEC_REQ}
!insertmacro ClearSectionFlag ${SEC_REQ} ${SECFLAGS_SELRO}
${EndIf}
${If} ${SectionIsSelected} ${SEC_OPT}
!insertmacro SetSectionFlag ${SEC_REQ} ${SECFLAGS_SELRO}
${Else}
!insertmacro ClearSectionFlag ${SEC_REQ} ${SF_RO}
${EndIf}
${If} ${SectionIsSelected} ${SEC_REQ}
!insertmacro SelectSection ${PRIVSEC_TOGGLESTATE}
${Else}
!insertmacro UnselectSection ${PRIVSEC_TOGGLESTATE}
${EndIf}
!undef SECFLAGS_SELRO
FunctionEnd
You can also set the sections as mandatory sections by using the SectionSetFlags command. You simply need to add it to the .onInit function as follows:
SectionGroup "group" Sec01
Section "required for section group"
SectionIn RO
SectionEnd
Section "optional" Sec02
SectionEnd
SectionGroupEnd
Function .onInit
SectionSetFlags ${Sec01} 17
FunctionEnd
This will gray "group" out, but it will be selected and installed as expected.
You can also setup a custom function which is being called when user clicks "Next" but no option is selected. In this function, you can check if any of Sections is selected or, as I did, check the variable which is set to "nothingSelected" on init, then modified when any section is created. If you put "Abort" in the custom function, the installer will stay on the same page. Here is a snippet:
page license
page directory
page components "" "" testSelection
page instfiles
function .onInit
StrCpy $somethingSelected "nothingSelected"
functionEnd
function testSelection
${If} $somethingSelected == "nothingSelected"
MessageBox MB_OK "Please select any option"
Abort
${EndIf}
functionEnd
function .onSelChange
#selecting mutually exclusive sections (removed for clarity)
StrCpy $somethingSelected $1
functionEnd

Resources