I want to apply onClick event on the combo box in nsis installer,so that I can execute a functionality on the click of button on combo box.
The OnClick event is not suitable for a combo-box, you want the OnChange event because the user can change it with the arrow keys etc.
To fire the change event programmatically you can call your handler directly or trick the combobox control:
!include nsDialogs.nsh
!include WinMessages.nsh
Page Custom MyPageCreate
Page InstFiles
Function OnComboChange
Pop $0
SendMessage $0 ${CB_GETCURSEL} "" "" $2
System::Call 'USER32::SendMessage(i$0,i${CB_GETLBTEXT},i$2,t.r2)'
MessageBox mb_ok "OnComboChange: $2"
FunctionEnd
Function EmulateChangeMethodA
Pop $0 ; Throw away parameter we don't care about
Push $1
Call OnComboChange
FunctionEnd
Function EmulateChangeMethodB
Pop $0 ; Throw away parameter we don't care about
FindWindow $0 "EDIT" "" $1
SendMessage $1 ${WM_COMMAND} 0x4000000 $0 ; Send WM_COMMAND,MAKELONG(0,EN_UPDATE),hwndEdit
FunctionEnd
Function EmulateChangeMethodC
Pop $0 ; Throw away parameter we don't care about
!ifndef CB_GETCOMBOBOXINFO
!define CB_GETCOMBOBOXINFO 0x0164
!endif
System::Call '*(&l4,&i16,&i16,i,i,i,i)i.r2'
SendMessage $1 ${CB_GETCOMBOBOXINFO} "" $2 ; This only works on Vista+?
System::Call '*$2(i,&i16,&i16,i,i,i,i.r0)'
System::Free $2
SendMessage $1 ${WM_COMMAND} 0x30000 $0 ; ; Send WM_COMMAND,MAKELONG(0,LBN_SELCANCEL),hwndList
FunctionEnd
Function MyPageCreate
nsDialogs::Create 1018
Pop $0
${NSD_CreateCombobox} 0 30u 100% 200u ""
Pop $1 ; This is the only control handle we care about in this example so make sure to never overwrite it!
${NSD_CB_AddString} $1 "Foo"
${NSD_CB_AddString} $1 "Bar"
SendMessage $1 ${CB_SETCURSEL} 0 ""
${NSD_OnChange} $1 OnComboChange
${NSD_CreateButton} 0 50u 33% 12u "Emulate change: A"
Pop $0
${NSD_OnClick} $0 EmulateChangeMethodA
${NSD_CreateButton} 33% 50u 33% 12u "Emulate change: B"
Pop $0
${NSD_OnClick} $0 EmulateChangeMethodB
${NSD_CreateButton} 66% 50u 33% 12u "Emulate change: C"
Pop $0
${NSD_OnClick} $0 EmulateChangeMethodC
nsDialogs::Show
FunctionEnd
Related
I need to add each element of the array to the Dialog menu. The problem is that with the command $ {array [#]} the dialog is recognizing each word as an element. Look:
First attempt: Adding element with spaces
list=('Google Chrome' 'Brackets' 'Visual Studio') # my list
declare -a dia # dialog array
for index in ${!list[#]}; do # for each index in the list
dia[${#dia[#]}]=$index # add index
dia[${#dia[#]}]=${list[$index]} #add element
done
dialog --menu "MENU" 0 0 0 $(echo ${dia[#]})
# Format: dialog --menu "TITLE" 0 0 0 'Index1' 'Element1' 'Index2' 'Element2'...
# dia[0] = 0
# dia[1] = 'Google Chrome'
# dia[1] = 1
# dia[2] = 'Brackets'...
PRINT: first attempt
I did this to avoid processing each word, with the return of $ {list [#]} separating word by word, obtaining a sequence of number and string.
Second attempt: Replace ' ' with '-'
for index in ${!list[#]}; do
dgl[${#dgl[#]}]=$index
dgl[${#dgl[#]}]=${list[$index]/' '/'-'}
done
PRINT: Second attempt
What I think is happening
I believe that when passing the elements of the array to the DIALOG command it considers spaces ("Google Chrome", for example). Is there a way for me to show this with spaces?
Made all necessary changes to your code so it now works as expected.
#!/usr/bin/env bash
list=('Google Chrome' 'Brackets' 'Visual Studio') # my list
declare -a dia=() # dialog array
for index in "${!list[#]}"; do # for each index in the list
dia+=("$index" "${list[index]}")
done
choice=$(
dialog --menu "MENU" 0 0 0 "${dia[#]}" \
3>&1 1>&2 2>&3 3>&- # Swap stdout with stderr to capture returned dialog text
)
dialog_rc=$? # Save the dialog return code
clear # restore terminal background and clear
case $dialog_rc in
0)
printf 'Your choice was: %s\n' "${list[choice]}"
;;
1)
echo 'Cancelled menu'
;;
255)
echo 'Closed menu without choice'
;;
*)
printf 'Unknown return code from dialog: %d\n' $dialog_rc >&2
;;
esac
I'm for hours trying to figure this one out.
What I would like to do is have a function that can receive N parameters which can include arrays.
Arrays can be in any parameter position, not only the last!
Order of parameters can change, for example, I can pass the first parameter as an array one time and in another call pass as second parameter!
Arrays and normal parameters must be interchangeable!
Example code (Note in this example parameter order is fixed, see following example for problem):
# $1 Menu title
# $2 List of menu options
# $3 Menu text
menu() {
dialog --clear --stdout --title "$1" --menu "$3" 0 0 0 "$2"
}
options=(
1 "Option 1"
2 "Option 2"
3 "Option 3"
4 "Option 4")
menu "Title" ${options[#]} "Text"
The first example can be solved like this:
# $1 Menu title
# $2 List of menu options
# $3 Menu text
menu() {
local -n a=$2
dialog --clear --stdout --title "$1" --menu "$3" 0 0 0 "${a[#]}"
}
options=(
1 "Option 1"
2 "Option 2"
3 "Option 3"
4 "Option 4")
menu "Title" options "Text"
Problem example:
my_func() {
my_command "$1" --something "$2" "$3" --somethingelse "$4"
}
optionsa=(
1 "Option 1"
2 "Option 2"
3 "Option 3"
4 "Option 4")
optionsb=(
1 "Option 1"
2 "Option 2"
3 "Option 3"
4 "Option 4")
my_func "Title" ${optionsa[#]} "Text" ${optionsb[#]}
# Note that I could do this and it must work too:
my_func ${optionsa[#]} "Title" ${optionsb[#]} "Text"
# This too must work:
my_func ${optionsa[#]} ${optionsb[#]} "Title" "Text"
When the array is expanded it must be possible to use the values as parameters to commands, if the value in the array has multiple words quoted ("Option 1") it must be treated as a single parameter to avoid for example passing a path as "/my dir/" and having it separated as </my> <dir/>.
How can I solve the second example, where the order or parameter can vary?
How can I solve the second example, where the order or parameter can vary?
There are two universal independent of programming language solutions to join a variadic length of a list of items together that any programmers should know about:
Pass the count...
my_func() {
local tmp optionsa title text optionsb
tmp=$1
shift
while ((tmp--)); do
optionsa+=("$1")
shift
done
title="$1"
shift
tmp=$1
shift
while ((tmp--)); do
optionsb+=("$1")
shift
done
text=$1
my_command "${optionsa[#]}" "$title" "${optionsb[#]}" "$text"
}
my_func 0 "Title" 0 "Text"
my_func 0 "Title" "${#optionsb[#]}" "${optionsb[#]}" "Text"
my_func "${#optionsa[#]}" "${optionsa[#]}" "Title" "${#optionsb[#]}" "${optionsb[#]}" "Text"
Use a sentinel value.
my_func() {
local tmp optionsa title text optionsb
while [[ -n "$1" ]]; do
optionsa+=("$1")
shift
done
title=$1
shift
while [[ -n "$1" ]]; do
optionsb+=("$1")
shift
done
text=$1
my_command "${optionsa[#]}" "$title" "${optionsb[#]}" "$text"
}
my_func "" "Title" "" "Text"
my_func "" "Title" "${optionsb[#]}" "" "Text"
my_func "${optionsa[#]}" "" "Title" "${optionsb[#]}" "" "Text"
And I see two bash specific solutions:
Pass arrays as names and use namereferences.
my_func() {
# Use unique names to avoid nameclashes
declare -n _my_func_optionsa=$1
local title=$2
declare -n _my_func_optionsb=$3
local title=$4
my_command "${_my_func_optionsa[#]}" "$title" "${_my_func_optionsb[#]}" "$text"
}
# arrays have to exists
my_func optionsa "Title" optionsb "Text"
Parse the arguments like a real man. This is actually universal solution, as it generally performs data serialization when creating list of arguments and then data deserialization when reading the arguments - the format (arguments as options) is specific to shell.
my_func() {
local args
# I am used to linux getopt, getopts would work as well
if ! args=$(getopt -n "$my_func" -o "a:t:b:x:" -- "$#"); then
echo "my_func: Invalid arguments" >&2
return 1
fi
set -- "$args"
local optionsa title optionsb text
while (($#)); do
case "$1" in
-a) optionsa+=("$2"); shift; ;;
-t) title="$2"; shift; ;;
-b) optionsb+=("$2"); shift; ;;
-x) text="$2"; shift; ;;
*) echo "my_func: Error parsing argument: $1" >&2; return 1; ;;
esac
shift
done
my_command "${optionsa[#]}" "$title" "${optionsb[#]}" "$text"
}
my_func -a opta1 -a opta2 -t Title -b optb1 -b optb2 -x text
# or build the options list from arrays:
# ie. perform data serialization
args=()
for i in "${optionsa[#]}"; do
args+=(-a "$i")
done
args+=(-t "title")
for i in "${optionsb[#]}"; do args+=(-b "$i"); done
args+=(-x "text")
my_func "${args[#]}"
Generally if a function has constant and small count of arguments, just use the arguments. If functions get complicated with more edge cases, I recommend to parse the arguments like a man - makes the function versatile and abstract, easy to expand and implement edge cases and handle corner cases and errors, easy to understand by other programmers, easily readable and parsable by human eyes.
Because your example codes may some problems, I recommend to research how does quoting work in shelll, specifically how "${array[#]}" differ from ${array[#]}, research how [#] differ from [*], how does and when word splitting expansion is performed and how it affects the parameters. All the unquoted array expansions in your code suffer from word splitting - the spaces will not be preserved, also in the first example.
How to customize NSIS checkbox in custom dialog using plugin or bitmap image?
Here is an example of NSIS installer with custom chechboxes
The current checkboxes working in options dialog only.
Maybe there's a way to do this by using OnClick event?
Any idea?
Thanks.
This is not a native NSIS feature but it can be implemented with some custom code:
!include WinMessages.nsh
!include nsDialogs.nsh ; WS_*
ShowInstDetails nevershow
Var Task1
Var Task2
Page InstFiles "" InitTasks FreeTasks
!macro SetTaskIcon hwnd icon
SetDetailsPrint none
Push $0
!if "${icon}" != ""
File "/oname=$PluginsDir\Task${hwnd}.ico" "${icon}"
System::Call 'USER32::LoadImage(i 0, t "$PluginsDir\Task${hwnd}.ico", i ${IMAGE_ICON}, i 32, i 32, i ${LR_LOADFROMFILE})i.r0'
SendMessage ${hwnd} ${STM_SETICON} $0 0 $0
!else
SendMessage ${hwnd} ${STM_GETICON} 0 0 $0
!endif
System::Call 'USER32::DestroyIcon(i $0)'
Pop $0
SetDetailsPrint lastused
!macroend
!macro FreeTask hwnd
!insertmacro SetTaskIcon ${hwnd} ""
!macroend
!macro CreateTask outvar icon text x y
System::Store S
FindWindow $1 "#32770" "" $HWNDPARENT
System::Call 'USER32::CreateWindowEx(i 0, t "Static", t "${text}", i ${WS_CHILD}|${WS_VISIBLE}|${SS_ICON}|${SS_REALSIZEIMAGE}, i ${x}, i ${y}, i 32, i 32, i r1, i 0, i 0, i 0)i.r0'
StrCpy ${outvar} $0
!insertmacro SetTaskIcon ${outvar} "${icon}"
IntOp $2 ${x} + 32
IntOp $2 $2 + 5 ; Padding
System::Call 'USER32::CreateWindowEx(i 0, t "Static", t "${text}", i ${WS_CHILD}|${WS_VISIBLE}|${SS_CENTERIMAGE}, i $2, i ${y}, i 999, i 32, i r1, i 0, i 0, i 0)i.r0'
SendMessage $1 ${WM_GETFONT} 0 0 $2
SendMessage $0 ${WM_SETFONT} $2 1
System::Store L
!macroend
Function InitTasks
!insertmacro CreateTask $Task1 "${NSISDIR}\Contrib\Graphics\Icons\llama-grey.ico" "Foo" 40 50
!insertmacro CreateTask $Task2 "${NSISDIR}\Contrib\Graphics\Icons\llama-grey.ico" "Bar" 40 90
FunctionEnd
Function FreeTasks
!insertmacro FreeTask $Task1
!insertmacro FreeTask $Task2
FunctionEnd
Section "Foo task"
Sleep 2222 ; Pretend to do some work
!insertmacro SetTaskIcon $Task1 "${NSISDIR}\Contrib\Graphics\Icons\llama-blue.ico"
SectionEnd
Section "Bar task"
Sleep 3333 ; Pretend to do some work
!insertmacro SetTaskIcon $Task2 "${NSISDIR}\Contrib\Graphics\Icons\llama-blue.ico"
SectionEnd
I want to delete all the elements from the combo box on click of the button in the nsis installer
nsDialogs does not have helper macros for every feature implemented by the Windows controls so sometimes you have to consult MSDN to find information about which message to send yourself.
!include nsDialogs.nsh
!include WinMessages.nsh
Page Custom MyPageCreate
Page InstFiles
Var MyComboHandle
Function ResetCombo
Pop $0
SendMessage $MyComboHandle ${CB_RESETCONTENT} 0 0
FunctionEnd
Function DeleteItemsButNotText
Pop $0
${NSD_GetText} $MyComboHandle $0
SendMessage $MyComboHandle ${CB_RESETCONTENT} 0 0
${NSD_SetText} $MyComboHandle $0
FunctionEnd
Function DeleteItemsButNotText_AlternateVersion
Pop $0
loop:
SendMessage $MyComboHandle ${CB_DELETESTRING} 0 0 $0 ; This will also clear the text if it matches the item
IntCmp $0 ${CB_ERR} "" loop loop
FunctionEnd
Function AddItems
Pop $0
${NSD_CB_AddString} $MyComboHandle "Foo"
${NSD_CB_AddString} $MyComboHandle "Bar"
SendMessage $MyComboHandle ${CB_SETCURSEL} 0 ""
FunctionEnd
Function MyPageCreate
nsDialogs::Create 1018
Pop $0
${NSD_CreateCombobox} 0 30u 100% 200u ""
Pop $MyComboHandle
Push ""
Call AddItems
${NSD_CreateButton} 0 50u 33% 12u "Reset"
Pop $0
${NSD_OnClick} $0 ResetCombo
${NSD_CreateButton} 33% 50u 33% 12u "Delete all items"
Pop $0
${NSD_OnClick} $0 DeleteItemsButNotText
${NSD_CreateButton} 66% 50u 33% 12u "Add items"
Pop $0
${NSD_OnClick} $0 AddItems
nsDialogs::Show
FunctionEnd
what I want to do is have a page where the user browses there filesystem for a specific value, and continues on if the filename matches.
To be specific, I would like the user to locate there php executable file, and also the directory I suppose(Not sure how I would extract the directory from the full path).
You can do this with a custom page:
!include nsDialogs.nsh
!include FileFunc.nsh
Page Custom MyPageCreate MyPageLeave
Var PhpPath
Function MyPageLeave
${NSD_GetText} $PhpPath $0
${GetFileName} $0 $1
${IfNot} ${FileExists} $0
${OrIf} $1 != "php.exe"
MessageBox mb_iconstop "You must locate php.exe to continue!"
Abort
${Else}
#php path is in $0, do something with it...
${EndIf}
FunctionEnd
Function MyPageComDlgSelectPHP
Pop $0
${NSD_GetText} $PhpPath $0
nsDialogs::SelectFileDialog open $0 "php.exe|php.exe"
Pop $0
${If} $0 != ""
${NSD_SetText} $PhpPath $0
${EndIf}
FunctionEnd
Function MyPageCreate
nsDialogs::Create 1018
Pop $0
${NSD_CreateText} 0 5u -25u 13u "$ProgramFiles\PHP\php.exe"
Pop $PhpPath
${NSD_CreateBrowseButton} -23u 4u 20u 15u "..."
Pop $0
${NSD_OnClick} $0 MyPageComDlgSelectPHP
nsDialogs::Show
FunctionEnd
or you can use the directory page:
!include LogicLib.nsh
Var PhpPath
Function .onInit
StrCpy $PhpPath "$ProgramFiles\PHP" ; Default (You could probably do better by checking the registry)
FunctionEnd
PageEx Directory
DirVar $PhpPath
DirVerify leave
PageCallbacks "" PhpPageShow PhpPageLeave
DirText "Select PHP folder" "PHP Folder" "" "Select PHP folder"
PageExEnd
Function PhpPageShow
;Hide space texts
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $1 $0 0x3FF
ShowWindow $1 0
GetDlgItem $1 $0 0x400
ShowWindow $1 0
FunctionEnd
Function PhpPageLeave
GetInstDirError $0
${If} $0 <> 0
${OrIfNot} ${FileExists} "$PhpPath\php.exe"
MessageBox mb_iconstop "You must locate the php folder to continue!"
Abort
${EndIf}
FunctionEnd
This can be achieved very easily with custom nsDialogs page and nsDialogs::SelectFileDialog which is designed for this purpose.
http://nsis.sourceforge.net/Docs/nsDialogs/Readme.html#ref-selectfiledialog