sl23 Posted 3 hours ago Posted 3 hours ago (edited) Please can someone help, I am so stuck i've spent all day today trying to resolve this and can't find the solution!!! I know my code is really probably screwed up, but I'm doing my best. Here is my current SettingsGUI.au3 expandcollapse popup#include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <MsgBoxConstants.au3> #include <Array.au3> #include <GuiListView.au3> #include <File.au3> #include <WinAPIFiles.au3> #include <GuiTab.au3> #include <GuiEdit.au3> #include <GuiButton.au3> #include <GuiComboBox.au3> #include <GuiListBox.au3> #include <GuiSlider.au3> #include <GuiMenu.au3> #include <Misc.au3> #include "Buttons.au3" Global $g_hGUI = 0, $g_hTab Global $g_aCheckBoxCtrlIDs[0], $g_aCheckBoxNames[0] Global $g_hSandBoxieInput, $hSandBoxieBrowse Global $hScanLV, $hScanLV_Handle, $btnAddScan, $btnEditScan, $btnDeleteScan, $btnDebugLV, $btnScan Global $g_aScanPaths[0], $g_aScanDepths[0] Global $g_AboutLinkURL = "https://www.portablefreeware.com/forums/viewtopic.php?p=109314#p109314" Global $g_AboutLinkCtrl Global $btnOK, $btnCancel Global $hButtonsLV = 0, $hButtonsLV_Handle = 0, $btnAddBtn = 0, $btnEditBtn = 0, $btnDeleteBtn = 0 Func _DebugListView() Local $selText = GUICtrlRead($hScanLV) MsgBox(64, "DEBUG", "GUICtrlRead result: " & $selText) If $selText <> "" Then Local $parts = StringSplit($selText, "|") If $parts[0] >= 3 Then MsgBox(64, "DEBUG", "Selected index: " & (Number($parts[1]) - 1) & @CRLF & _ "Path: " & $parts[2] & @CRLF & "Depth: " & $parts[3]) EndIf EndIf EndFunc Func ShowSettingsGUI() Global $g_hGUI, $g_SettingsOpen $g_SettingsOpen = True ;~ TrayItemSetState($TRAY_SETTINGS, $TRAY_DISABLE) $g_hGUI = GUICreate("Settings", 600, 550, -1, -1, $WS_SYSMENU) $g_hTab = GUICtrlCreateTab(10, 10, 680, 460) ; --- Global Tab --- GUICtrlCreateTabItem("Global") SetupGlobalTab() ; --- Buttons Tab (NEW TAB #2) --- GUICtrlCreateTabItem("Buttons") SetupButtonsTab() ; --- About Tab (now TAB #3) --- GUICtrlCreateTabItem("About") SetupAboutTab() GUICtrlCreateTabItem("") ; End tab items ; OK and Cancel buttons - Always visible, not part of tabs $btnOK = GUICtrlCreateButton("OK", 400, 480, 80, 30) $btnCancel = GUICtrlCreateButton("Cancel", 490, 480, 80, 30) GUISetState(@SW_SHOW, $g_hGUI) While 1 Local $msg = GUIGetMsg() Switch $msg Case $GUI_EVENT_CLOSE ExitLoop ; Add any button handlers for the Buttons tab here, e.g.: ; Case $btnMyButton ; ; Do something Case $btnAddScan _AddScanPath() Case $btnEditScan _EditScanPath() Case $btnDeleteScan _DeleteScanPath() Case $btnDebugLV _DebugListView() Case $btnScan SaveScanPathsToINI() ScanAppsFolders() Case $hSandBoxieBrowse Local $exePath = FileOpenDialog("Select SandMan.exe (Sandboxie)", @ScriptDir, "Executable (*.exe)", 1) If @error Or $exePath = "" Then ContinueLoop GUICtrlSetData($g_hSandBoxieInput, $exePath) Case $g_AboutLinkCtrl ShellExecute($g_AboutLinkURL) ; --- BUTTONS TAB EVENTS --- Case $btnAddBtn AddButtonSection() RefreshButtonListView() ;~ Case $btnEditBtn ;~ EditButtonSection() Case $btnDeleteBtn DeleteButtonSection() RefreshButtonListView() ; ListView item clicked - handle here! Case $hButtonsLV Local $idx = GetSelectedButtonIndex() If $idx <> -1 Then Local $section = GetButtonSectionByIndex($idx) PopulateFieldsFromSection($section) EndIf Case $btnOK SaveGlobalTab() SaveScanPathsToINI() ExitLoop Case $btnCancel ExitLoop Case $msg = $hButtonsLV Local $idx = GetSelectedButtonIndex() If $idx <> -1 Then Local $section = GetButtonSectionByIndex($idx) PopulateFieldsFromSection($section) EndIf EndSwitch WEnd GUIDelete($g_hGUI) ;~ TrayItemSetState($TRAY_SETTINGS, $TRAY_ENABLE) EndFunc Func SetupGlobalTab() Local $iY = 50 GUICtrlCreateLabel("Scan Paths", 20, $iY, 100, 20) $hScanLV = GUICtrlCreateListView("#|Path|Depth", 20, $iY + 20, 490, 180, $LVS_SHOWSELALWAYS + $LVS_SINGLESEL) $hScanLV_Handle = GUICtrlGetHandle($hScanLV) _GUICtrlListView_SetColumnWidth($hScanLV_Handle, 0, 40) _GUICtrlListView_SetColumnWidth($hScanLV_Handle, 1, 370) ; Path column enlarged! _GUICtrlListView_SetColumnWidth($hScanLV_Handle, 2, 50) $btnAddScan = GUICtrlCreateButton("Add", 520, $iY + 25, 50, 25) $btnEditScan = GUICtrlCreateButton("Edit", 520, $iY + 60, 50, 25) $btnDeleteScan = GUICtrlCreateButton("Delete", 520, $iY + 95, 50, 25) $btnDebugLV = GUICtrlCreateButton("Debug LV", 520, $iY + 130, 60, 25) $btnScan = GUICtrlCreateButton("Scan", 520, $iY + 170, 50, 25) GUICtrlSetState($btnDebugLV, $GUI_HIDE) Local $iY2 = $iY + 210 Local $aSettings = IniReadSection(@ScriptDir & "\App\Settings.ini", "GLOBAL") If @error Then Return Global $g_aCheckBoxCtrlIDs[0], $g_aCheckBoxNames[0] For $i = 1 To $aSettings[0][0] Local $key = $aSettings[$i][0] Local $val = $aSettings[$i][1] If StringRegExp($key, "^Scan\d+$") Or StringRegExp($key, "^Scan\d+Depth$") Then ContinueLoop EndIf If $val = "1" Or $val = "0" Then Local $id = GUICtrlCreateCheckbox($key, 30, $iY2, 200, 20) GUICtrlSetState($id, ($val = "1") ? $GUI_CHECKED : $GUI_UNCHECKED) _ArrayAdd($g_aCheckBoxCtrlIDs, $id) _ArrayAdd($g_aCheckBoxNames, $key) $iY2 += 30 EndIf Next GUICtrlCreateLabel("Sandboxie Executable Path (SandMan.exe):", 30, $iY2, 220, 20) Local $sandboxieValue = IniRead(@ScriptDir & "\App\Settings.ini", "GLOBAL", "SandboxiePath", "") $g_hSandBoxieInput = GUICtrlCreateInput($sandboxieValue, 30, $iY2 + 25, 250, 20) $hSandBoxieBrowse = GUICtrlCreateButton("...", 250, $iY2 -4, 30, 22) _LoadScanPathsFromINI() _RefreshScanListView() EndFunc Func SaveGlobalTab() For $i = 0 To UBound($g_aCheckBoxCtrlIDs) - 1 Local $state = GUICtrlRead($g_aCheckBoxCtrlIDs[$i]) Local $val = ($state = $GUI_CHECKED) ? "1" : "0" IniWrite(@ScriptDir & "\App\Settings.ini", "GLOBAL", $g_aCheckBoxNames[$i], $val) Next Local $sandboxiePath = GUICtrlRead($g_hSandBoxieInput) IniWrite(@ScriptDir & "\App\Settings.ini", "GLOBAL", "SandboxiePath", $sandboxiePath) EndFunc Func _LoadScanPathsFromINI() Global $g_aScanPaths[0], $g_aScanDepths[0] Local $i = 1 While True Local $path = IniRead(@ScriptDir & "\App\Settings.ini", "GLOBAL", "Scan" & $i, "") If $path = "" Then ExitLoop Local $depth = IniRead(@ScriptDir & "\App\Settings.ini", "GLOBAL", "Scan" & $i & "Depth", "1") _ArrayAdd($g_aScanPaths, $path) _ArrayAdd($g_aScanDepths, $depth) $i += 1 WEnd EndFunc ; --- New combined dialog for path/depth --- Func ShowScanPathDialog($sPath = "", $sDepth = "1", $bEdit = False) Local $title = $bEdit ? "Edit Scan Path" : "Add Scan Path" Local $hDlg = GUICreate($title, 365, 150, -1, -1, $WS_SYSMENU) GUICtrlCreateLabel("Scan Path:", 10, 14, 70, 22) Local $inpPath = GUICtrlCreateInput($sPath, 80, 10, 220, 20) Local $btnBrowse = GUICtrlCreateButton("...", 310, 9, 40, 22) GUICtrlCreateLabel("Scan Depth:", 10, 50, 70, 22) Local $cboDepth = GUICtrlCreateCombo("", 80, 46, 60, 22) GUICtrlCreateLabel("1 = Scan path folder, 2 = Sub folder, etc.", 150, 50, 200, 22) For $i = 1 To 10 GUICtrlSetData($cboDepth, $i) Next GUICtrlSetData($cboDepth, $sDepth) Local $btnOK = GUICtrlCreateButton("OK", 180, 85, 80, 30) Local $btnCancel = GUICtrlCreateButton("Cancel", 270, 85, 80, 30) GUISetState(@SW_SHOW, $hDlg) Local $result[2] = ["", ""] While 1 Local $msg = GUIGetMsg() Select Case $msg = $GUI_EVENT_CLOSE Or $msg = $btnCancel ExitLoop Case $msg = $btnBrowse Local $selectedFolder = FileSelectFolder("Select folder for Scan Path", "", 1) If Not @error And $selectedFolder <> "" Then GUICtrlSetData($inpPath, $selectedFolder) EndIf Case $msg = $btnOK $result[0] = GUICtrlRead($inpPath) Local $depth = GUICtrlRead($cboDepth) ; Auto-correct depth between 1 and 10 If Not StringIsInt($depth) Then $depth = 1 ElseIf $depth < 1 Then $depth = 1 ElseIf $depth > 10 Then $depth = 10 EndIf $result[1] = $depth ExitLoop EndSelect WEnd GUIDelete($hDlg) Return $result EndFunc Func _AddScanPath() Local $vals = ShowScanPathDialog("", "1", False) If $vals[0] = "" Then Return If $vals[1] = "" Then $vals[1] = "1" _ArrayAdd($g_aScanPaths, $vals[0]) _ArrayAdd($g_aScanDepths, $vals[1]) _RefreshScanListView() EndFunc Func _GetSelectedIndex() Local $count = _GUICtrlListView_GetItemCount($hScanLV_Handle) For $i = 0 To $count - 1 If _GUICtrlListView_GetItemSelected($hScanLV_Handle, $i) Then Return $i EndIf Next Return -1 EndFunc Func _EditScanPath() Local $selIndex = _GetSelectedIndex() If $selIndex = -1 Then MsgBox(16, "Edit Scan Path", "Please select a scan path to edit.") Return EndIf Local $currPath = $g_aScanPaths[$selIndex] Local $currDepth = $g_aScanDepths[$selIndex] Local $vals = ShowScanPathDialog($currPath, $currDepth, True) If $vals[0] = "" Then Return If $vals[1] = "" Then $vals[1] = "1" $g_aScanPaths[$selIndex] = $vals[0] $g_aScanDepths[$selIndex] = $vals[1] _RefreshScanListView() EndFunc Func _DeleteScanPath() Local $selIndex = _GetSelectedIndex() If $selIndex = -1 Then MsgBox(16, "Delete Scan Path", "Please select a scan path to delete.") Return EndIf _ArrayDelete($g_aScanPaths, $selIndex) _ArrayDelete($g_aScanDepths, $selIndex) _RefreshScanListView() EndFunc Func _RefreshScanListView() _GUICtrlListView_DeleteAllItems($hScanLV_Handle) For $i = 0 To UBound($g_aScanPaths) - 1 GUICtrlCreateListViewItem(StringFormat("%d|%s|%s", $i+1, $g_aScanPaths[$i], $g_aScanDepths[$i]), $hScanLV) Next EndFunc Func SaveScanPathsToINI() Local $i = 1 While True Local $old = IniRead(@ScriptDir & "\App\Settings.ini", "GLOBAL", "Scan" & $i, "") If $old = "" Then ExitLoop IniDelete(@ScriptDir & "\App\Settings.ini", "GLOBAL", "Scan" & $i) IniDelete(@ScriptDir & "\App\Settings.ini", "GLOBAL", "Scan" & $i & "Depth") $i += 1 WEnd For $j = 0 To UBound($g_aScanPaths) - 1 IniWrite(@ScriptDir & "\App\Settings.ini", "GLOBAL", "Scan" & ($j + 1), $g_aScanPaths[$j]) IniWrite(@ScriptDir & "\App\Settings.ini", "GLOBAL", "Scan" & ($j + 1) & "Depth", $g_aScanDepths[$j]) Next EndFunc Func SetupAboutTab() Local $startY = 50 ; Move About tab content down so it doesn't overlap tab header! GUICtrlCreatePic(@ScriptDir & "\App\AxiomTrayJPG.jpg", 20, $startY + 25, 64, 64) Local $lblTitle = GUICtrlCreateLabel("Axiom Tray Launcher", 110, $startY, 250, 30) GUICtrlSetFont($lblTitle, 16, 800, 0, "Segoe UI") Local $aAboutLabels[6] $aAboutLabels[0] = GUICtrlCreateLabel($g_CurrentVersion, 110, $startY + 35, 250, 20) $aAboutLabels[1] = GUICtrlCreateLabel("Author: sl23", 110, $startY + 60, 250, 20) $aAboutLabels[2] = GUICtrlCreateLabel("Build date: 2025-08-17", 110, $startY + 85, 250, 20) $aAboutLabels[3] = GUICtrlCreateLabel("A tray menu to launch your portable apps.", 110, $startY + 110, 250, 20) $aAboutLabels[4] = GUICtrlCreateLabel("Website: " & $g_AboutLinkURL, 110, $startY + 135, 550, 20) $aAboutLabels[5] = GUICtrlCreateButton("Visit Website", 150, $startY + 165, 180, 25) $g_AboutLinkCtrl = $aAboutLabels[5] ; set global button ID For $i = 0 To UBound($aAboutLabels) - 1 GUICtrlSetFont($aAboutLabels[$i], 10) Next EndFunc ;~ If True Then ShowSettingsGUI() And the include Buttons.au3 expandcollapse popup#include <GuiListView.au3> #include <Array.au3> #include <File.au3> #include <GuiEdit.au3> #include <GuiComboBox.au3> #include <GuiButton.au3> ;~ Global Const $INI_FILE = @ScriptDir & "\App\Settings.ini" Global $g_aButtonSections[0] Global $g_ButtonKeysOrder = [ _ "ButtonText", "RunFile", "RunAsAdmin", "NetAccess", "SingleInstance", "Env_APPDATA", "SymLink", "Splash", "SplashWait", "Sandboxie", "SandboxName", "WorkDir", "Arguments", "Category" _ ] Global $g_ButtonSettingComments = [ _ "ButtonText", "Button Caption/Text", _ "RunFile", "Executable file to run (absolute or relative)", _ "RunAsAdmin", "Run as Admin (1=Yes, 0=No)", _ "NetAccess", "Network Access (1=Yes, 0=No)", _ "SingleInstance", "Only allow single instance (1=Yes, 0=No)", _ "Env_APPDATA", "Custom AppData path", _ "SymLink", "Symlink source|destination", _ "Splash", "Splash image filename", _ "SplashWait", "Splash image wait time (ms)", _ "Sandboxie", "Sandboxie executable path", _ "SandboxName", "Sandboxie sandbox name", _ "WorkDir", "Working directory", _ "Arguments", "Command-line arguments", _ "Category", "Button category" _ ] Global $hButtonsLV = 0, $hButtonsLV_Handle = 0, $btnAddBtn = 0, $btnEditBtn = 0, $btnDeleteBtn = 0 Global $g_fields[UBound($g_ButtonKeysOrder)], $g_checkFields[UBound($g_ButtonKeysOrder)], $g_selectedIndex = -1 Func SetupButtonsTab() Local $iY = 50 GUICtrlCreateLabel("Buttons:", 20, $iY, 140, 20) $hButtonsLV = GUICtrlCreateListView("#|Name", 20, $iY + 20, 220, 350, $LVS_SHOWSELALWAYS + $LVS_SINGLESEL) ConsoleWrite("ListView Control ID: " & $hButtonsLV & @CRLF) ConsoleWrite("ListView Handle: " & GUICtrlGetHandle($hButtonsLV) & @CRLF) $hButtonsLV_Handle = GUICtrlGetHandle($hButtonsLV) $btnAddBtn = GUICtrlCreateButton("Add", 45, $iY + 380, 50, 25) $btnEditBtn = GUICtrlCreateButton("Edit", 105, $iY + 380, 50, 25) $btnDeleteBtn = GUICtrlCreateButton("Delete", 165, $iY + 380, 50, 25) Local $rightX = 270 Local $y = $iY Local $fieldOffset = 4 ; How many pixels upwards to move the field boxes For $i = 0 To UBound($g_ButtonKeysOrder) - 1 Local $key = $g_ButtonKeysOrder[$i] Local $tip = GetSettingComment($key) GUICtrlCreateLabel($key, $rightX, $y, 90, 20) $g_checkFields[$i] = False If StringRegExp($key, "RunAsAdmin|NetAccess|SingleInstance") Then $g_fields[$i] = GUICtrlCreateCheckbox("", $rightX + 100, $y - $fieldOffset, 20, 20) $g_checkFields[$i] = True Else $g_fields[$i] = GUICtrlCreateInput("", $rightX + 100, $y - $fieldOffset, 200, 20) EndIf GUICtrlSetTip($g_fields[$i], $tip) $y += 28 Next ; Load button sections from INI and update ListView LoadButtonSectionsFromINI() ;~ RefreshButtonListView() EndFunc Func LoadButtonSectionsFromINI() Global $g_aButtonSections[0] Local $aSections = IniReadSectionNames($INI_FILE) If @error Or Not IsArray($aSections) Then Return Local $aButtonSections[1][2] $aButtonSections[0][0] = 0 For $i = 1 To $aSections[0] If StringRegExp($aSections[$i], "^BUTTON\d+$") Then Local $num = Number(StringRegExpReplace($aSections[$i], "[^\d]", "")) $aButtonSections[0][0] += 1 ReDim $aButtonSections[$aButtonSections[0][0]+1][2] $aButtonSections[$aButtonSections[0][0]][0] = $num $aButtonSections[$aButtonSections[0][0]][1] = $aSections[$i] EndIf Next If $aButtonSections[0][0] > 1 Then _ArraySort($aButtonSections, 0, 1) EndIf Global $g_aButtonSections[0] For $i = 1 To $aButtonSections[0][0] _ArrayAdd($g_aButtonSections, $aButtonSections[$i][1]) Next EndFunc Func __ButtonSectionCompare($a, $b) Local $numA = StringRegExpReplace($a, "[^\d]", "") Local $numB = StringRegExpReplace($b, "[^\d]", "") Return Number($numA) - Number($numB) EndFunc Func RefreshButtonListView() _GUICtrlListView_DeleteAllItems($hButtonsLV_Handle) For $i = 0 To UBound($g_aButtonSections) - 1 Local $section = $g_aButtonSections[$i] ; Extract the number from "BUTTONN" Local $num = "" Local $matches = StringRegExp($section, "^BUTTON(\d+)$", 1) If IsArray($matches) Then $num = $matches[0] EndIf ; Read ButtonText value Local $txt = IniRead($INI_FILE, $section, "ButtonText", "") GUICtrlCreateListViewItem($num & "|" & $txt, $hButtonsLV) Next EndFunc Func PopulateFieldsFromSection($section) For $i = 0 To UBound($g_ButtonKeysOrder) - 1 Local $val = IniRead($INI_FILE, $section, $g_ButtonKeysOrder[$i], "") If $g_checkFields[$i] Then GUICtrlSetState($g_fields[$i], ($val = "1") ? $GUI_CHECKED : $GUI_UNCHECKED) Else GUICtrlSetData($g_fields[$i], $val) EndIf Next EndFunc Func ClearFields() For $i = 0 To UBound($g_ButtonKeysOrder) - 1 If $g_checkFields[$i] Then GUICtrlSetState($g_fields[$i], $GUI_UNCHECKED) Else GUICtrlSetData($g_fields[$i], "") EndIf Next EndFunc Func SaveFieldsToSection($section) For $i = 0 To UBound($g_ButtonKeysOrder) - 1 Local $val If $g_checkFields[$i] Then $val = GUICtrlRead($g_fields[$i]) = $GUI_CHECKED ? "1" : "0" Else $val = GUICtrlRead($g_fields[$i]) EndIf IniWrite($INI_FILE, $section, $g_ButtonKeysOrder[$i], $val) Next EndFunc Func GetSelectedButtonIndex() Local $count = _GUICtrlListView_GetItemCount($hButtonsLV_Handle) For $i = 0 To $count - 1 If _GUICtrlListView_GetItemSelected($hButtonsLV_Handle, $i) Then Return $i EndIf Next Return -1 EndFunc Func GetButtonSectionByIndex($idx) If $idx >= 0 And $idx < UBound($g_aButtonSections) Then Return $g_aButtonSections[$idx] EndIf Return "" EndFunc Func AddButtonSection() MsgBox(0, "Add", "New Button# created.") Local $n = 1 While 1 Local $section = "BUTTON" & $n IniReadSection($INI_FILE, $section) If @error Then ExitLoop $n += 1 WEnd Local $newSection = "BUTTON" & $n For $i = 0 To UBound($g_ButtonKeysOrder) - 1 Local $key = $g_ButtonKeysOrder[$i] Local $default = "" Switch $key Case "ButtonText" $default = "New Button" Case "RunAsAdmin", "NetAccess", "SingleInstance" $default = "0" Case Else $default = "" EndSwitch IniWrite($INI_FILE, $newSection, $key, $default) Next LoadButtonSectionsFromINI() $g_selectedIndex = UBound($g_aButtonSections) - 1 If $g_selectedIndex >= 0 Then Local $section = GetButtonSectionByIndex($g_selectedIndex) _GUICtrlListView_SetItemSelected($hButtonsLV_Handle, $g_selectedIndex, True, True) EndIf EndFunc ;~ Func EditButtonSection() ;~ Local $section, $isAdd = ($g_selectedIndex = -1) ;~ If $isAdd Then ;~ Local $nextNum = NextButtonSectionNumber() ;~ $section = "BUTTON" & $nextNum ;~ SaveFieldsToSection($section) ;~ Else ;~ $section = GetButtonSectionByIndex($g_selectedIndex) ;~ SaveFieldsToSection($section) ;~ EndIf ;~ LoadButtonSectionsFromINI() ;~ RefreshButtonListView() ;~ EndFunc Func DeleteButtonSection() ; Always get selected index from ListView at the time of button click Local $idx = GetSelectedButtonIndex() If $idx = -1 Then MsgBox(16, "Delete Button", "Please select a button to delete.") Return EndIf Local $section = GetButtonSectionByIndex($idx) Local $answer = MsgBox(36, "Delete Button", "Delete section [" & $section & "]?") If $answer <> 6 Then Return ; 6 = Yes IniDelete($INI_FILE, $section) LoadButtonSectionsFromINI() ;~ RefreshButtonListView() ClearFields() $g_selectedIndex = -1 EndFunc Func NextButtonSectionNumber() Local $maxNum = 0 For $i = 0 To UBound($g_aButtonSections) - 1 Local $m = StringRegExp($g_aButtonSections[$i], "^BUTTON(\d+)$", 1) If IsArray($m) And Number($m[0]) > $maxNum Then $maxNum = Number($m[0]) Next Return $maxNum + 1 EndFunc Func GetSettingComment($key) For $i = 0 To UBound($g_ButtonSettingComments) - 2 Step 2 If $g_ButtonSettingComments[$i] = $key Then Return $g_ButtonSettingComments[$i + 1] Next Return "" EndFunc Thank you for your help 😁 Edited 3 hours ago by sl23
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now