tests/nsis3/share/nsis/Include/Memento.nsh

Summary

Maintainability
Test Coverage
!verbose push
!verbose 3

!include LogicLib.nsh
!include Sections.nsh

!ifndef ___MEMENTO_NSH___
!define ___MEMENTO_NSH___

#####################################
### Memento                       ###
#####################################

/*

Memento is a set of macros that allow installers to remember user selection
across separate runs of the installer. Currently, it can remember the state
of sections and mark new sections as bold. In the future, it'll integrate
InstallOptions and maybe even the Modern UI.

A usage example can be found in `Examples\Memento.nsi`.

*/

#####################################
### Usage Instructions            ###
#####################################

/*

1. Declare usage of Memento by including Memento.nsh at the top of the script.

      !include Memento.nsh

2. Define MEMENTO_REGISTRY_ROOT and MEMENTO_REGISTRY_KEY with the a registry key
   where sections' state should be saved.

      !define MEMENTO_REGISTRY_ROOT HKLM
      !define MEMENTO_REGISTRY_KEY \
                Software\Microsoft\Windows\CurrentVersion\Uninstall\MyProgram

3. Replace Section with ${MementoSection} and SectionEnd with ${MementoSectionEnd}
   for sections that whose state should be remembered by Memento.

   For sections that should be unselected by default, use ${MementoSection}'s
   brother - ${MementoUnselectedSection}.

   Sections that don't already have an identifier must be assigned one.

   Section identifiers must stay the same across different versions of the
   installer or their state will be forgotten.

4. Use ${MementoSectionDone} after the last ${MementoSection}.

5. Add a call to ${MementoSectionRestore} to .onInit to restore the state
   of all sections from the registry.

      Function .onInit

        ${MementoSectionRestore}

      FunctionEnd

6. Add a call to ${MementoSectionSave} to .onInstSuccess to save the state
   of all sections to the registry.

      Function .onInstSuccess

        ${MementoSectionSave}

      FunctionEnd

7. Tattoo the location of the chosen registry key on your arm.

*/

#####################################
### User API                      ###
#####################################

;
; ${MementoSection}
;
;   Defines a section whose state is remembered by Memento.
;
;   Usage is similar to Section.
;
;     ${MementoSection} "name" "some_id"
;

!define MementoSection "!insertmacro MementoSection"

;
; ${MementoSectionEnd}
;
;   Ends a section previously opened using ${MementoSection}.
;
;   Usage is similar to SectionEnd.
;
;     ${MementoSection} "name" "some_id"
;        # some code...
;     ${MementoSectionEnd}
;

;
; ${MementoUnselectedSection}
;
;   Defines a section whose state is remembered by Memento and is
;   unselected by default.
;
;   Usage is similar to Section with the /o switch.
;
;     ${MementoUnselectedSection} "name" "some_id"
;

!define MementoUnselectedSection "!insertmacro MementoUnselectedSection"

;
; ${MementoSectionEnd}
;
;   Ends a section previously opened using ${MementoSection}.
;
;   Usage is similar to SectionEnd.
;
;     ${MementoSection} "name" "some_id"
;        # some code...
;     ${MementoSectionEnd}
;

!define MementoSectionEnd "!insertmacro MementoSectionEnd"

;
; ${MementoSectionDone}
;
;   Used after all ${MementoSection} have been set.
;
;     ${MementoSection} "name1" "some_id1"
;        # some code...
;     ${MementoSectionEnd}
;
;     ${MementoSection} "name2" "some_id2"
;        # some code...
;     ${MementoSectionEnd}
;
;     ${MementoSection} "name3" "some_id3"
;        # some code...
;     ${MementoSectionEnd}
;
;     ${MementoSectionDone}
;

!define MementoSectionDone "!insertmacro MementoSectionDone"

;
; ${MementoSectionRestore}
;
;   Restores the state of all Memento sections from the registry.
;
;   Commonly used in .onInit.
;
;     Function .onInit
;
;       ${MementoSectionRestore}
;
;     FunctionEnd
;

!define MementoSectionRestore "!insertmacro MementoSectionRestore"

;
; ${MementoSectionSave}
;
;   Saves the state of all Memento sections to the registry.
;
;   Commonly used in .onInstSuccess.
;
;     Function .onInstSuccess
;
;       ${MementoSectionSave}
;
;     FunctionEnd
;

!define MementoSectionSave "!insertmacro MementoSectionSave"


#####################################
### Internal Defines              ###
#####################################

!define __MementoSectionIndex 1

#####################################
### Internal Macros               ###
#####################################

!macro __MementoCheckSettings

  !ifndef MEMENTO_REGISTRY_ROOT | MEMENTO_REGISTRY_KEY

    !error "MEMENTO_REGISTRY_ROOT and MEMENTO_REGISTRY_KEY must be defined before using any of Memento's macros"

  !endif

!macroend

!macro __MementoSection flags name id

  !insertmacro __MementoCheckSettings

  !ifndef __MementoSectionIndex

    !error "MementoSectionDone already used!"

  !endif

  !define __MementoSectionLastSectionId `${id}`

  !verbose pop

  Section ${flags} `${name}` `${id}`

  !verbose push
  !verbose 3

!macroend

#####################################
### User Macros                   ###
#####################################

!macro MementoSection name id

  !verbose push
  !verbose 3

  !insertmacro __MementoSection "" `${name}` `${id}`

  !verbose pop

!macroend

!macro MementoUnselectedSection name id

  !verbose push
  !verbose 3

  !insertmacro __MementoSection /o `${name}` `${id}`

  !define __MementoSectionUnselected

  !verbose pop

!macroend

!macro MementoSectionEnd

  SectionEnd

  !verbose push
  !verbose 3

  !insertmacro __MementoCheckSettings

  !ifndef __MementoSectionIndex

    !error "MementoSectionDone already used!"

  !endif

  !define /MATH __MementoSectionIndexNext \
      ${__MementoSectionIndex} + 1

  Function __MementoSectionMarkNew${__MementoSectionIndex}

    ClearErrors
    ReadRegDWORD $0 ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` `MementoSection_${__MementoSectionLastSectionId}`

    ${If} ${Errors}

      !insertmacro SetSectionFlag `${${__MementoSectionLastSectionId}}` ${SF_BOLD}

    ${EndIf}

    GetFunctionAddress $0 __MementoSectionMarkNew${__MementoSectionIndexNext}
    Goto $0

  FunctionEnd

  Function __MementoSectionRestoreStatus${__MementoSectionIndex}

    ClearErrors
    ReadRegDWORD $0 ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` `MementoSection_${__MementoSectionLastSectionId}`

    !ifndef __MementoSectionUnselected

      ${If} ${Errors}
      ${OrIf} $0 != 0

        !insertmacro SelectSection `${${__MementoSectionLastSectionId}}`

      ${Else}

        !insertmacro UnselectSection `${${__MementoSectionLastSectionId}}`

      ${EndIf}

    !else

      !undef __MementoSectionUnselected

      ${If} ${Errors}
      ${OrIf} $0 == 0

        !insertmacro UnselectSection `${${__MementoSectionLastSectionId}}`

      ${Else}

        !insertmacro SelectSection `${${__MementoSectionLastSectionId}}`

      ${EndIf}

    !endif

    GetFunctionAddress $0 __MementoSectionRestoreStatus${__MementoSectionIndexNext}
    Goto $0

  FunctionEnd

  Function __MementoSectionSaveStatus${__MementoSectionIndex}

    ${If} ${SectionIsSelected} `${${__MementoSectionLastSectionId}}`

      WriteRegDWORD ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` `MementoSection_${__MementoSectionLastSectionId}` 1

    ${Else}

      WriteRegDWORD ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` `MementoSection_${__MementoSectionLastSectionId}` 0

    ${EndIf}

    GetFunctionAddress $0 __MementoSectionSaveStatus${__MementoSectionIndexNext}
    Goto $0

  FunctionEnd

  !undef __MementoSectionIndex
  !define __MementoSectionIndex ${__MementoSectionIndexNext}
  !undef __MementoSectionIndexNext

  !undef __MementoSectionLastSectionId

  !verbose pop

!macroend

!macro MementoSectionDone

  !verbose push
  !verbose 3

  !insertmacro __MementoCheckSettings

  Function __MementoSectionMarkNew${__MementoSectionIndex}
  FunctionEnd

  Function __MementoSectionRestoreStatus${__MementoSectionIndex}
  FunctionEnd

  Function __MementoSectionSaveStatus${__MementoSectionIndex}
  FunctionEnd

  !undef __MementoSectionIndex

  !verbose pop

!macroend

!macro MementoSectionRestore

  !verbose push
  !verbose 3

  !insertmacro __MementoCheckSettings

  Push $0
  Push $1
  Push $2
  Push $3

    # check for first usage

    ClearErrors

    ReadRegStr $0 ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` MementoSectionUsed

    ${If} ${Errors}

      # use script defaults on first run
      Goto done

    ${EndIf}

    # mark new components in bold
    
    Call __MementoSectionMarkNew1

    # mark section groups in bold

    StrCpy $0 0
    StrCpy $1 ""
    StrCpy $2 ""
    StrCpy $3 ""

    loop:

      ClearErrors

      ${If} ${SectionIsBold} $0

        ${If} $1 != ""

          !insertmacro SetSectionFlag $1 ${SF_BOLD}

        ${EndIf}

        ${If} $2 != ""

          !insertmacro SetSectionFlag $2 ${SF_BOLD}

        ${EndIf}

        ${If} $3 != ""

          !insertmacro SetSectionFlag $3 ${SF_BOLD}

        ${EndIf}

      ${ElseIf} ${Errors}

        Goto loop_end

      ${EndIf}

      ${If} ${SectionIsSectionGroup} $0

        ${If} $1 == ""

          StrCpy $1 $0

        ${ElseIf} $2 == ""

          StrCpy $2 $0

        ${ElseIf} $3 == ""

          StrCpy $3 $0

        ${EndIf}

      ${EndIf}

      ${If} ${SectionIsSectionGroupEnd} $0

        ${If} $3 != ""

          StrCpy $3 ""

        ${ElseIf} $2 != ""

          StrCpy $2 ""

        ${ElseIf} $1 != ""

          StrCpy $1 ""

        ${EndIf}

      ${EndIf}

      IntOp $0 $0 + 1

    Goto loop
    loop_end:

    # restore sections' status

    Call __MementoSectionRestoreStatus1

  # all done

  done:

  Pop $3
  Pop $2
  Pop $1
  Pop $0

  !verbose pop

!macroend

!macro MementoSectionSave

  !verbose push
  !verbose 3

  !insertmacro __MementoCheckSettings

  Push $0

    WriteRegStr ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` MementoSectionUsed ""
  
    Call __MementoSectionSaveStatus1

  Pop $0

  !verbose pop

!macroend



!endif # ___MEMENTO_NSH___

!verbose pop