!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" "id"
;
;     ${MementoSectionEx} "[flags]" "name" "memento_id" "section_id"
;

!define MementoSection "!insertmacro MementoSection"
!define MementoSectionEx "!insertmacro MementoSectionEx"

;
; ${MementoSectionEnd}
;
;   Ends a section previously opened using ${MementoSection}.
;
;   Usage is similar to SectionEnd.
;
;     ${MementoSection} "name" "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" "id"
;

!define MementoUnselectedSection "!insertmacro MementoUnselectedSection"

;
; ${MementoSectionEnd}
;
;   Ends a section previously opened using ${MementoSection}.
;
;   Usage is similar to SectionEnd.
;
;     ${MementoSection} "name" "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"

;
; MementoSection<ReadWrite><Int|Marker>
;
; Replaceable macros that allow custom storage methods to be used.
;
!define /IfNDef MementoSectionStorageSeparator '_'

!ifmacrondef MementoSectionReadInt
!define __MementoSectionStdRegReadWrite
!define /IfNDef MementoSectionStoragePrefix "MementoSection"

!macro MementoSectionReadInt outvar name
ReadRegDWord ${outvar} ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` `${MementoSectionStoragePrefix}${name}`
!macroend

!macro MementoSectionWriteInt name val
WriteRegDWord ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` `${MementoSectionStoragePrefix}${name}` `${val}`
!macroend

!macro MementoSectionReadMarker outvar name
ReadRegStr ${outvar} ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` `${MementoSectionStoragePrefix}${name}`
!macroend

!macro MementoSectionWriteMarker name
WriteRegStr ${MEMENTO_REGISTRY_ROOT} `${MEMENTO_REGISTRY_KEY}` `${MementoSectionStoragePrefix}${name}` ``
!macroend

!endif

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

!define __MementoSectionIndex 1

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

!macro __MementoCheckSettings

  !ifdef __MementoSectionStdRegReadWrite
    !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
  !endif

!macroend

!macro __MementoSection flags name mementoid sectionid

  !insertmacro __MementoCheckSettings

  !ifndef __MementoSectionIndex

    !error "MementoSectionDone already used!"

  !endif

  !define __MementoSectionLastMementoId `${mementoid}`
  !define __MementoSectionLastSectionId `${sectionid}`
  !define __MementoSectionLastFlags `${flags}`

  !verbose pop

  Section ${flags} `${name}` `${sectionid}`

  !verbose push
  !verbose 3

!macroend

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

!macro MementoSectionEx flags name mid sid
  
  !verbose push 3

  !insertmacro __MementoSection `${flags}` `${name}` `${mid}` `${sid}`

  !verbose pop

!macroend

!macro MementoSection name id

  !verbose push 3

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

  !verbose pop

!macroend

!macro MementoUnselectedSection name id

  !verbose push 3

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

  !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
    !insertmacro MementoSectionReadInt $0 `${MementoSectionStorageSeparator}${__MementoSectionLastMementoId}`

    ${If} ${Errors}

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

    ${EndIf}

    GetFunctionAddress $0 __MementoSectionMarkNew${__MementoSectionIndexNext}
    Goto $0

  FunctionEnd

  Function __MementoSectionRestoreStatus${__MementoSectionIndex}

    ClearErrors
    !insertmacro MementoSectionReadInt $0 `${MementoSectionStorageSeparator}${__MementoSectionLastMementoId}`

    !searchparse /ignorecase /noerrors `${__MementoSectionLastFlags}-` `/o` __MementoSectionUnselected

    !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}}`

      !insertmacro MementoSectionWriteInt `${MementoSectionStorageSeparator}${__MementoSectionLastMementoId}` 1

    ${Else}

      !insertmacro MementoSectionWriteInt `${MementoSectionStorageSeparator}${__MementoSectionLastMementoId}` 0

    ${EndIf}

    GetFunctionAddress $0 __MementoSectionSaveStatus${__MementoSectionIndexNext}
    Goto $0

  FunctionEnd

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

  !undef __MementoSectionLastMementoId
  !undef __MementoSectionLastSectionId
  !undef __MementoSectionLastFlags

  !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
    !insertmacro MementoSectionReadMarker $0 `Used`

    ${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

    !insertmacro MementoSectionWriteMarker `Used`
  
    Call __MementoSectionSaveStatus1

  Pop $0

  !verbose pop

!macroend



!endif # ___MEMENTO_NSH___

!verbose pop
