/* Copyright (C) 2026 The Qt Company Ltd.
 *
 * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
*/

/*!
    \previouspage ifw-component-description.html
    \page noninteractive.html
    \nextpage scripting.html

    \title Controller Scripting

    For each installer, you can specify a control script that interacts with certain parts of
    the installer's UI or functionality. The control script can add and remove pages to the
    wizard, change existing pages, do additional checks, and interact with the UI by
    simulating user clicks. This allows for example unattended installations.

    The script format has to be compatible with QJSEngine.

    This section describes the functions that are called to implement such a control script.
    It also gives an overview of installer pages and the widgets that are available on each
    page, such as push buttons, radio buttons, and line edits.

    \section1 Writing Control Scripts

    A minimal valid script needs to contain at least a constructor, which can look like this:
    \code
        function Controller()
        {
        }
    \endcode

    The following example presents a more advanced script that uses the \l gui
    JavaScript global object methods to set a new page title and welcome message
    on the introduction page and to automatically click the \uicontrol Next
    button on the target directory page:

    \code
        function Controller()
        {
        }

        Controller.prototype.IntroductionPageCallback = function()
        {
            var widget = gui.currentPageWidget(); // get the current wizard page
            if (widget != null) {
                widget.title = "New title."; // set the page title
                widget.MessageLabel.setText("New Message."); // set the welcome text
            }
        }

        Controller.prototype.TargetDirectoryPageCallback = function()
        {
            gui.clickButton(buttons.NextButton); // automatically click the Next button
        }
    \endcode

    For more information about the JavaScript global objects that you can use
    in control scripts, see \l{Scripting API}.

    In addition to the predefined global objects, the scripting API supports working
    with other objects derived from \c QObject. In the above code example the
    \c{gui.currentPageWidget()} method returns a widget of type \c QWidget.

    The scripting API makes use of Qt's object trees. Widgets derived from \c QObject export
    their named child objects as properties for the JavaScript object, where the name of the
    property is the same as the child object's \c{QObject::objectName}. The default properties
    and their access functions of objects derived from \c QObject can also be used in the scripts.

    For example, in the above code the \c MessageLabel object from class \c QLabel is a child
    of the \c widget. The \c setText() is the setter access function for its \c QLabel::text property.

    In addition to properties, the signals and public slots of objects derived from \c QObject
    can be used in both controller and component scripts.

    \section1 Predefined Installer Pages

    The QInstaller JavaScript object provides access to the following predefined
    installer pages:

    \list
        \li \c Introduction
        \li \c TargetDirectory
        \li \c ComponentSelection
        \li \c LicenseCheck
        \li \c StartMenuSelection
        \li \c ReadyForInstallation
        \li \c PerformInstallation
        \li \c InstallationFinished
    \endlist

    The \l buttons JavaScript object provides a set of buttons that can be used
    on installer pages.

    The following sections describe the functions that you can implement to
    interact with installer pages and the widgets that are available on each
    page.

    \section2 Introduction Page

    Implement the \c Controller.prototype.IntroductionPageCallback() function to interact with
    widgets on the introduction page.

    Wizard buttons:
    \list
        \li \c NextButton
        \li \c CancelButton
    \endlist

    \table
        \header
        \li Widgets
        \li Brief Description

        \row
        \li \c ErrorLabel
        \li Displays an error message.

        \row
        \li \c MessageLabel
        \li Displays a message. By default, it displays the
            "Welcome to the \l{ProductNameTarget}{<Name>} Setup" message.

        \row
        \li \c InformationLabel
        \li Displays progress information.
    \endtable

    \table
        \header
        \li Radio Buttons
        \li Brief Description

        \row
        \li \c PackageManagerRadioButton
        \li The package manager radio button shown on the page while running as \MT.

        \row
        \li \c UpdaterRadioButton
        \li The updater radio button shown on the page while running as \MT.

        \row
        \li \c UninstallerRadioButton
        \li The uninstaller radio button shown on the page while running as \MT.
            Selected by default.
    \endtable

    \table
        \header
        \li Progress Bar
        \li Brief Description

        \row
        \li \c InformationProgressBar
        \li The progress bar shown while fetching remote packages.
    \endtable

    \table
        \header
        \li Qt Core Feature
        \li Brief Description

        \row
        \li \c packageManagerCoreTypeChanged()
        \li Connect to this signal if you want to be notified when the type of \MT
            changes.
            \note The signal is only emitted when the user has started the binary as so called
            \MT (after the installation) and switches between the radio buttons.
    \endtable

    Example code:
    \code
    function Controller()
    {
        var widget = gui.pageById(QInstaller.Introduction); // get the introduction wizard page
        if (widget != null)
            widget.packageManagerCoreTypeChanged.connect(onPackageManagerCoreTypeChanged);
    }

    onPackageManagerCoreTypeChanged = function()
    {
        console.log("Is Updater: " + installer.isUpdater());
        console.log("Is Uninstaller: " + installer.isUninstaller());
        console.log("Is Package Manager: " + installer.isPackageManager());
    }
    \endcode


    \section2 License Agreement Page

    Implement the \c Controller.prototype.LicenseAgreementPageCallback() function to interact with
    widgets on the license agreement page.

    Wizard buttons:
    \list
        \li \c NextButton
        \li \c CancelButton
        \li \c BackButton
    \endlist

    \table
        \header
        \li Widgets
        \li Brief Description

        \row
        \li \c LicenseInfoLabel
        \li Additional info label on top of the page. Empty by default.

        \row
        \li \c LicenseListWidget
        \li Lists the available licenses.

        \row
        \li \c LicenseTextBrowser
        \li Shows the content of the selected license file.

        \row
        \li \c AcceptLicenseLabel
        \li Shows the text next to the accept license check box.

        \row
        \li \c AcceptLicenseCheckBox
        \li Accepts the license agreement.
    \endtable

    \section2 Target Directory Page

    Implement the \c Controller.prototype.TargetDirectoryPageCallback() function to interact with
    widgets on the target directory selection page.

    Wizard buttons:
    \list
        \li \c NextButton
        \li \c CancelButton
        \li \c BackButton
    \endlist

    \table
        \header
        \li Widgets
        \li Brief Description

        \row
        \li \c MessageLabel
        \li Displays a message.

        \row
        \li \c TargetDirectoryLineEdit
        \li Displays the value of the installation's target directory.

        \row
        \li \c WarningLabel
        \li Displays a warning.
    \endtable

    \section2 Component Selection Page

    Implement the \c Controller.prototype.ComponentSelectionPageCallback() function to interact
    with widgets on the component selection page.

    Wizard buttons:
    \list
        \li \c NextButton
        \li \c CancelButton
        \li \c BackButton
    \endlist

    \table
        \header
        \li Methods
        \li Brief Description

        \row
            \li \c selectAll()
            \li Selects all available packages if possible.

        \row
            \li \c deselectAll()
            \li Deselects all available packages if possible.

        \row
            \li \c selectDefault()
            \li Resets the checked state of available packages to their initial state.

        \row
            \li \c selectComponent(id)
            \li Selects the package with \c id (string).

        \row
            \li \c deselectComponent(id)
            \li Deselects the package with \c id (string).
    \endtable

    \table
        \header
        \li Push Buttons
        \li Brief Description

        \row
            \li \c SelectAllComponentsButton
            \li Selects all available packages if possible.

        \row
            \li \c DeselectAllComponentsButton
            \li Deselects all available packages if possible.

        \row
            \li \c SelectDefaultComponentsButton
            \li Resets the checked state of available packages to their initial state.

        \row
            \li \c ResetComponentsButton
            \li Resets to already installed components.

        \row
            \li \c FetchCategoryButton
            \li Fetch components from a category.
    \endtable

    \table
        \header
        \li Widgets
        \li Brief Description

        \row
            \li \c CategoryGroupBox
            \li Contains checkboxes for selecting repository categories.
    \endtable

    Installer Framework 3.1 introduces repository categories as a new feature. When
    you use an installer that contains repository categories, you can select a category
    by its display name, fetch its contents, and then select the included components for installation.

    You can fetch the components from a category as follows:
    \code
    Controller.prototype.ComponentSelectionPageCallback = function()
    {
        var page = gui.pageWidgetByObjectName("ComponentSelectionPage");

        // if CategoryGroupBox is visible, check one of the checkboxes
        // and click fetch button before selecting any components
        var groupBox = gui.findChild(page, "CategoryGroupBox");
        if (groupBox) {
            console.log("groupBox found");
            // findChild second argument is the display name of the checkbox
            var checkBox = gui.findChild(page, "Archive");
            if (checkBox) {
                console.log("checkBox found");
                console.log("checkBox name: " + checkBox.text);
                if (checkBox.checked == false) {
                    checkBox.click();
                    var fetchButton = gui.findChild(page, "FetchCategoryButton");
                    if (fetchButton) {
                        console.log("fetchButton found");
                        fetchButton.click();
                    } else {
                        console.log("fetchButton NOT found");
                    }
                }
            } else {
                console.log("checkBox NOT found");
            }
        } else {
            console.log("groupBox NOT found");
        }
        // you can now select components from the fetched category
    }
    \endcode

    \section2 Start Menu Directory Page

    Implement the \c Controller.prototype.StartMenuDirectoryPageCallback() function to interact with
    widgets on the ready for installation page.

    Wizard buttons:
    \list
        \li \c NextButton
        \li \c CancelButton
        \li \c BackButton
    \endlist

    \table
        \header
        \li Widgets
        \li Brief Description

        \row
        \li \c StartMenuPathLineEdit
        \li Shows the directory where to create the program's shortcut.
    \endtable


    \section2 Ready for Installation Page

    Implement the \c Controller.prototype.ReadyForInstallationPageCallback() function to interact
    with widgets on the ready for installation page.

    Wizard buttons:
    \list
        \li \c CommitButton
        \li \c CancelButton
        \li \c BackButton
    \endlist

    \table
        \header
        \li Widgets
        \li Brief Description

        \row
        \li \c InstallMsgLabel
        \li Title for InstallComponentsTreeview.

        \row
        \li \c InstallComponentsTreeview
        \li Displays a summary of components to be installed.

        \row
        \li \c UninstallMsgLabel
        \li Title for UninstallComponentsTreeview.

        \row
        \li \c UninstallComponentsTreeview
        \li Displays a summary of components to be uninstalled.

        \row
        \li \c ComponentSummaryScrollArea
        \li Contains the area where both treeviews and message labels are drawn.

        \row
        \li \c RemoveAllMsgLabel
        \li Displays information about full uninstall.
    \endtable

    \section2 Perform Installation Page

    Implement the \c Controller.prototype.PerformInstallationPageCallback() function to interact
    with widgets on the perform installation page.

    Wizard buttons:
    \list
        \li \c CommitButton
        \li \c CancelButton
    \endlist


    \section2 Finished Page

    Implement the \c Controller.prototype.FinishedPageCallback() function to interact with widgets
    on the installation finished page.

    Wizard buttons:
    \list
        \li \c CommitButton
        \li \c CancelButton
        \li \c FinishButton
    \endlist

    \table
        \header
        \li Widgets
        \li Brief Description

        \row
        \li \c MessageLabel
        \li Displays a message.

        \row
        \li \c RunItCheckBox
        \li Text field that informs users that they can start an application after
            the installation process has finished.
    \endtable


    \section1 Custom Pages

    Custom pages are registered as \c{Dynamic${ObjectName}}, where \c{${ObjectName}} is the object
    name set in the UI file. Thus, the \c{Dynamic${ObjectName}Callback()} function is called.
    Widgets can be addressed using their object names (from the UI file).

    Example code:
    \code
    function Component()
    {
        // add page with widget \c SomePageWidget before the target directory page
        installer.addWizardPage(component, "SomePageWidget", QInstaller.TargetDirectory)
    }

    Component.prototype.DynamicSomePageWidgetCallback = function()
    {
        var page = gui.pageWidgetByObjectName("DynamicSomePageWidget");
        page.myButton.click, //direct child of the UI file's widget
        page.someFancyWidget.subWidget.setText("foobar") // nested widget
    }
    \endcode


    \section1 Message Boxes

    While executing the installer application, for example, the application
    might show some message boxes about an error that occurred. This is fine
    while running the application on the end user's system, but it might break
    automated test suites. To overcome this issue, all message
    boxes shown by the Qt Installer Framework are addressable by a specific identifier.

    \table
        \header
        \li Identifier
        \li Possible Answers
        \li Description

        \row
        \li \c OverwriteTargetDirectory
        \li Yes, No
        \li Confirmation for using an already existing directory as the target directory for
            installation.

        \row
        \li \c installationError
        \li OK, Retry, Ignore
        \li A fatal error occurred while performing the installation.

        \row
        \li \c installationErrorWithRetry
        \li Retry, Ignore, Cancel
        \li An error occurred while performing the installation. End users can
            select \uicontrol Retry to
            try again.

        \row
        \li \c AuthorizationError
        \li Abort, OK
        \li Elevated permissions could not be acquired.

        \row
        \li \c OperationDoesNotExistError
        \li Abort, Ignore
        \li An error occurred while trying to perform an operation, but the operation did not exist.

        \row
        \li \c isAutoDependOnError
        \li OK
        \li An error occurred while calling a package script. It was not possible to evaluate
            if the package has a auto dependency on other packages.

        \row
        \li \c isDefaultError
        \li OK
        \li An error occurred while calling a package script. It was not possible to evaluate
            if the package will be installed by default.

        \row
        \li \c DownloadError
        \li Retry, Cancel
        \li An error occurred while downloading an archive hash from a remote
            repository. End users can select \uicontrol Retry to try again.

        \row
        \li \c archiveDownloadError
        \li Retry, Cancel
        \li An error occurred while downloading a archive from a remote repository. End users can
            select \uicontrol Retry to try again.

        \row
        \li \c WriteError
        \li OK
        \li An error occurred while writing the \MT.

        \row
        \li \c ElevationError
        \li OK
        \li Elevated permissions could not be acquired.

        \row
        \li \c unknown
        \li OK
        \li An unknown error occurred while removing a certain package.

        \row
        \li \c Error
        \li OK
        \li Generic error.

        \row
        \li \c stopProcessesForUpdates
        \li Retry, Ignore, Cancel
        \li An error occurred while updating a package. Some running application or process needs
            to be quit before the update can be performed. End users can select
            \uicontrol Retry to try
            again once they have been stopped.

        \row
        \li \c Installer_Needs_To_Be_Local_Error
        \li OK
        \li The installer binary was started from a network location, but the installation over
            network is not supported.

        \row
        \li \c TargetDirectoryInUse
        \li No
        \li The installation's target directory already contains an installation.

        \row
        \li \c WrongTargetDirectory
        \li OK
        \li The installation's target directory is a file or symlink.

        \row
        \li \c AlreadyRunning
        \li OK
        \li Another application instance is already running.
    \endtable

    Example code:
    \code
    function Controller()
    {
        installer.autoRejectMessageBoxes;
        installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes);
        installer.setMessageBoxAutomaticAnswer("stopProcessesForUpdates", QMessageBox.Ignore);
    }
    \endcode
*/
