/**
 * File:	Wizard.ycp
 * Package:	yast2
 * Summary:	Wizard dialog
 * Authors:	Stefan Hundhammer <sh@suse.de>
 *
 * $Id: Wizard.ycp 22710 2005-03-21 15:27:30Z sh $
 *
 * Provides the wizard dialog (common screen for all YaST2 installation
 * modules) and functions to set the contents, to replace and restore
 * special widgets.
 */

{

    module "Wizard";
    textdomain "base";

    import "Label";
    import "Popup";
    import "Directory";

    // keep trailing "/" !!
    string theme_path = Directory::themedir + "/current";
    string icon_path  = theme_path + "/icons/22x22/apps";

    boolean have_fancy_ui_cache = nil;

    // this variable is set from Product:: constructor
    // to setup correct &product; macro in UI
    string product_name = "";

    boolean haveFancyUI()
    {
	if ( have_fancy_ui_cache == nil )
	{
	    map ui_info = UI::GetDisplayInfo();

	    have_fancy_ui_cache = UI::HasSpecialWidget(`Wizard ) == true
		&& ui_info[ "Depth" ]:0 >= 15
		&& ui_info[ "DefaultWidth" ]:0 >= 800
		&& ui_info[ "DefaultHeight"]:0 >= 600;

	    // have_fancy_ui_cache = false;

	    UI::SetFunctionKeys( Label::DefaultFunctionKeyMap() );
	}

	return have_fancy_ui_cache;
    }


    /**
     * Returns a button box with buttons "Back", "Abort", "Next"
     * @return a widget tree
     **/
    global term BackAbortNextButtonBox() {
	return `HBox(
		     `HWeight(1, `ReplacePoint(`id(`rep_back ), `PushButton(`id(`back ), `opt (`key_F8), Label::BackButton()  ) ) ),
		     `HStretch(),
		     `ReplacePoint(`id(`rep_abort), `PushButton(`id(`abort), `opt (`key_F9), Label::AbortButton() ) ),
		     `HStretch(),
		     `HWeight(1, `ReplacePoint(`id(`rep_next ), `PushButton(`id(`next), `opt (`key_F10, `default), Label::NextButton()  ) ) )
		     );
    }


    /**
     * Returns a button box with buttons "Back", "Abort Installation", "Next"
     * @return a widget tree
     **/
    global term BackAbortInstallationNextButtonBox()
    {
	return `HBox(
		     `HWeight(1, `ReplacePoint(`id(`rep_back ), `PushButton(`id(`back ), `opt (`key_F8), Label::BackButton()  ) ) ),
		     `HStretch(),
		     `ReplacePoint(`id(`rep_abort), `PushButton(`id(`abort), `opt (`key_F9), Label::AbortInstallationButton() ) ),
		     `HStretch(),
		     `HWeight(1, `ReplacePoint(`id(`rep_next ), `PushButton(`id(`next), `opt (`key_F10, `default), Label::NextButton()  ) ) )
		     );
    }


    /**
     * Returns a button box with buttons "Back", "Next"
     * @return a widget tree
     **/
    global term BackNextButtonBox() {
	return `HBox(
		     `HWeight(1, `ReplacePoint(`id(`rep_back ), `PushButton(`id(`back ), `opt (`key_F8), Label::BackButton()  ) ) ),
		     `HStretch(),
		     `HWeight(1, `ReplacePoint(`id(`rep_next ), `PushButton(`id(`next ), `opt (`key_F10, `default), Label::NextButton()  ) ) )
		     );
    }


    /**
     * Returns a button box with buttons "Cancel", "Accept"
     * @return a widget tree
     **/
    global term CancelAcceptButtonBox() {
	return `HBox(
		     `HWeight(1, `Empty() ),	// Layout trick to make sure the center button is centered
		     `HStretch(),
		     `HWeight(1, `PushButton(`id(`cancel ), `opt (`key_F9), Label::CancelButton()  ) ),
		     `HStretch(),
		     `HWeight(1, `PushButton(`id(`accept ), `opt (`key_F10, `default), Label::AcceptButton()  ) )
		     );
    }


    /**
     * Returns a button box with buttons "Abort", "Accept"
     * @return a widget tree
     **/
    global term AbortAcceptButtonBox() {
	return `HBox(
		     `HWeight(1, `ReplacePoint (`id (`back_rep), `Empty())),	// Layout trick to make sure the center button is centered
		     `HStretch(),
		     `PushButton(`id(`abort ), `opt (`key_F9), Label::AbortButton() ),
		     `HStretch(),
		     `HWeight(1, `PushButton(`id(`accept ), `opt (`key_F10, `default), Label::AcceptButton()  ) )
		     );
    }


    /**
     * Returns a button box with buttons "Abort Installation", "Accept"
     * @return a widget tree
     **/
    global term AbortInstallationAcceptButtonBox() {
	return `HBox(
		     `HWeight(1, `Empty() ),	// Layout trick to make sure the center button is centered
		     `HStretch(),
		     `PushButton(`id(`abort ), `opt (`key_F9), Label::AbortInstallationButton() ),
		     `HStretch(),
		     `HWeight(1, `PushButton(`id(`accept ), `opt (`key_F10, `default), Label::AcceptButton()  ) )
		     );
    }


    /**
     * Returns a button box with buttons "Abort", "Apply", "Finish"
     * @return a widget tree
     **/
    global term AbortApplyFinishButtonBox()
    {
	return `HBox(
		     `PushButton(`id(`abort ), Label::AbortButton() ),
		     `HStretch(),
		     // button text
		     `PushButton(`id(`apply), _("&Apply") ),
		     `HStretch(),
		     `PushButton(`id(`finish), Label::FinishButton() )
		     );
    }


    /**
     * @short Create a Generic Dialog
     *
     * @descr Returns a term describing a generic wizard dialog with a configurable
     * button box.
     *
     * @param	button_box term that contains a `HBox() with buttons in it
     * @return	term term describing the dialog.
     **/
    global term GenericDialog( term button_box ) {
	return `VBox( `id(`WizardDialog),
                      `ReplacePoint( `id(`topmenu ), `Empty() ),
		      `HBox(
			    `HWeight( 30, `ReplacePoint( `id(`helpSpace), `RichText(`id(`HelpText), ""))),
			    `HSpacing(1),
			    `HWeight( 70, `VBox(
						`VSpacing(0.2),
						`HBox(
						      `Heading(`id(`title), `opt (`hstretch), _("YaST2\nInitializing ...")),
						      `HStretch()
						      ),
						`VWeight( 1, // Layout trick: Lower layout priority with weight
							 `HVCenter(
								   `ReplacePoint(`id(`contents), `Empty() )
								   )
							 ),
						`ReplacePoint(`id(`rep_button_box), button_box ),
						`VSpacing(0.2)
						)
				      ),
			    `HSpacing(1)
			    )
		      );
    }



    /**
     * Check if the topmost dialog is a wizard dialog
     * (i.e. has a widget with `id(`WizardDialog) )
     *
     * @return boolean True if topmost dialog is a wizard dialog, false otherwise
     **/
    global boolean IsWizardDialog() {
	return
	    UI::WidgetExists(`id(`WizardDialog) ) == true ||
	    UI::WidgetExists(`wizard) == true;
    }


    /**
     * Open a popup dialog that displays a help text (rich text format).
     *
     * @param help_text the text to display
     **/
    global void ShowHelp( string help_text ) {
	Popup::LongText(
			  // Heading for help popup window
			  _("Help"),
			  `RichText( help_text ),
			  50, 20 );
    }


    /**
     * Returns a standard wizard dialog with buttons "Next", "Back", "Abort".
     * @return term describing the dialog.
     **/
    global term NextBackDialog() {
	return GenericDialog( BackAbortNextButtonBox() );
    }


    /**
     * Returns a standard wizard dialog with buttons "Cancel", "Accept"
     * @return term describing the dialog.
     **/
    global term AcceptDialog() {
	return GenericDialog( CancelAcceptButtonBox() );
    }


    /**
     * Open any wizard dialog.
     * @param	dialog	a wizard dialog, e.g. Wizard::GenericDialog()
     **/
    global void OpenDialog( term dialog ) {
	UI::OpenDialog( `opt(`defaultsize), dialog );
    }


    /**
     * Open a dialog with buttons "Next", "Back", "Abort"
     * and set the keyboard focus to "Next".
     **/
    global void OpenNextBackDialog()
    {
	if ( haveFancyUI() )
	{
	    UI::OpenDialog(`opt(`defaultsize ),
			   `Wizard(`back,  Label::BackButton(),
				   `abort, Label::AbortButton(),
				   `next,  Label::NextButton() ) );
	}
	else
	{
	    Wizard::OpenDialog( NextBackDialog() );
	    UI::SetFocus(`id(`next) );
	}
    }


    /**
     * Open a dialog with "Accept", "Cancel"
     * and set the keyboard focus to "Accept".
     **/
    global void OpenAcceptDialog()
    {
	if ( haveFancyUI() )
	{
	    UI::OpenDialog(`opt(`defaultsize ),
			   `Wizard(`no_back_button,   	"",
				   `cancel,		Label::CancelButton(),
				   `accept,		Label::AcceptButton() ) );

	    // Don't let sloppy calls to Wizard::SetContents() disable this button by accident
	    UI::WizardCommand(`ProtectNextButton( true ) );
	}
	else
	{
	    Wizard::OpenDialog( AcceptDialog() );
	    UI::SetFocus(`id(`accept) );
	}
    }


    /**
     * Open a dialog with "Accept", "Cancel"
     * and set the keyboard focus to "Accept".
     **/
    global void OpenAbortApplyFinishDialog()
    {
	if ( haveFancyUI() )
	{
	    UI::OpenDialog(`opt(`defaultsize ),
			   `Wizard(`apply,	_( "&Apply" ),
				   `abort,	Label::AbortButton(),
				   `finish,	Label::FinishButton() ) );
	}
	else
	{
	    Wizard::OpenDialog( GenericDialog( AbortApplyFinishButtonBox() ) );
	    UI::SetFocus(`id(`finish ) );
	}
    }


    /**
     * Open a dialog with "Accept", "Cancel" that will also accept workflow steps.
     **/
    global void OpenAcceptStepsDialog()
    {
	if ( haveFancyUI() )
	{
	    UI::OpenDialog(`opt(`defaultsize ),
			   `Wizard(`opt(`stepsEnabled),
				   `no_back_button,	"",
				   `cancel,		Label::CancelButton(),
				   `accept,		Label::AcceptButton() ) );

	    // Don't let sloppy calls to Wizard::SetContents() disable this button by accident
	    UI::WizardCommand(`ProtectNextButton( true ) );
	}
	else
	{
	    OpenAcceptDialog();
	}
    }



    /**
     * Open a dialog with "Accept", "Cancel" that will also accept workflow steps.
     **/
    global void OpenAcceptAbortStepsDialog()
    {
	if ( haveFancyUI() )
	{
	    UI::OpenDialog(`opt(`defaultsize ),
			   `Wizard(`opt(`stepsEnabled),
				   `no_back_button,   	"",
				   `abort,		Label::AbortButton(),
				   `accept,		Label::AcceptButton() ) );
 
	    // Don't let sloppy calls to Wizard::SetContents() disable this button by accident
	    UI::WizardCommand(`ProtectNextButton( true ) );
	}
	else
	{
	    Wizard::OpenDialog( GenericDialog( AbortAcceptButtonBox() ) );
	}
    }


    /**
     * Open a dialog with "Back", "Next", "Abort" that will also accept workflow steps.
     **/
    global void OpenNextBackStepsDialog()
    {
	if ( haveFancyUI() )
	{
	    UI::OpenDialog(`opt(`defaultsize ),
			   `Wizard(`opt(`stepsEnabled),
				   `back,  Label::BackButton(),
				   `abort, Label::AbortButton(),
				   `next,  Label::NextButton() ) );
	}
	else
	{
	    OpenNextBackDialog();
	}
    }


    /**
     * Open a wizard dialog with simple layout
     *
     * no graphics, no steps,
     * only a help widget buttons (by default "Back", "Abort", "Next").
     *
     * This is the only type of wizard dialog which still allows replacing
     * the help space - either already upon opening it or later with
     * Wizard::ReplaceCustomHelp().
     *
     * If help_space_contents is 'nil', the normal help widget will remain.
     * If button_box is 'nil', Wizard::BackAbortNextButtonBox() is used.
     * @param help_space_contents Help space contents
     * @param button_box Buttom Box
     * @return void
     **/
    global void OpenCustomDialog( term help_space_contents,
					 term button_box )
    {
	if ( button_box == nil )
	    button_box = BackAbortNextButtonBox();

	UI::OpenDialog(`opt(`defaultsize),
		       GenericDialog( button_box ) );

	if ( help_space_contents != nil )
	    UI::ReplaceWidget(`id(`helpSpace), help_space_contents );
    }


    /**
     * Replace the help widget for dialogs opened with Wizard::OpenCustomDialog().
     * @param contents Replace custom help with supplied contents
     **/
    global void ReplaceCustomHelp( term contents )
    {
	if ( UI::WidgetExists( `id(`helpSpace ) ) )
	{
	    UI::ReplaceWidget(`id(`helpSpace), contents);
	}
	else
	{
	    y2error( "Wizard::ReplaceHelpSpace() works only for dialogs opened with Wizard::OpenSimpleDialog() !" );
	}
    }


    /**
     * Close a wizard dialog.
     **/
    global void CloseDialog() {
	if ( IsWizardDialog() )
	{
	    UI::CloseDialog();
	}
	else
	{
	    y2error( "Wizard::CloseDialog(): Topmost dialog is not a wizard dialog!" );
	}
    }


    /**
     * Substitute for UI::UserInput
     *
     * This function transparently handles different
     * variations of the wizard layout. Returns `next if `next or `accept were
     * clicked, `back if `back or `cancel were clicked. Simply replace
     *    ret = UI::UserInput()
     * with
     *    ret = Wizard::UserInput()
     *
     * @return (maybe normalized) widget ID
     **/
    global any UserInput() {
	any input = UI::UserInput();

	if ( input == `accept )	return `next;
	if ( input == `cancel )	return `back;

	return input;
    }


    /**
     * Create and open a typical installation wizard dialog.
     *
     * For backwards compatibility only - don't use this any more in new modules.
     **/
    global void CreateDialog()
    {
        // Set productname for help text
        UI::SetProductName(product_name);

	Wizard::OpenNextBackDialog();
    }


    /**
     * Set the contents of a wizard dialog
     *
     * How the general framework for the installation wizard should
     * look like. This function creates and shows a dialog.
     *
     * @param title Dialog Title
     * @param contents The Dialog contents
     * @param help_text Help text
     * @param has_back Is the Back button enabled?
     * @param has_next Is the Next button enabled?
     * @example_file ../examples/wizard1.ycp
     * @screenshot screenshots/wizard1.png
     **/
    global void SetContents(string title, term contents, string help_text,
				   boolean has_back, boolean has_next) {

	if ( UI::WizardCommand(`SetDialogHeading( title ) ) == true )
	{
	    UI::WizardCommand(`SetHelpText( help_text ) );
	    UI::WizardCommand(`EnableNextButton( has_next ) );
	    UI::WizardCommand(`EnableBackButton( has_back ) );
	    UI::WizardCommand(`SetFocusToNextButton() );
	}
	else
	{
	    if ( UI::WidgetExists(`id(`next ) ) )
	    {
		UI::ChangeWidget(`id(`next), `Enabled, has_next);
		UI::SetFocus( `id(`next) );
	    }

	    if ( UI::WidgetExists(`id(`back	) ) )	UI::ChangeWidget(`id(`back), `Enabled, has_back);
	    if ( UI::WidgetExists(`id(`accept	) ) )	UI::SetFocus( `id(`accept ) );
	    if ( UI::WidgetExists(`id(`abort	) ) )	UI::ChangeWidget(`id(`abort),`Enabled, true);
	    if ( UI::WidgetExists(`id(`HelpText	) ) )	UI::ChangeWidget(`id(`HelpText), `Value, help_text);
	    if ( UI::WidgetExists(`id(`title	) ) )	UI::ChangeWidget(`id(`title), `Value, title);
	}

	UI::ReplaceWidget(`id(`contents), contents);
    }


    /**
     * Clear the wizard contents.
     *
     * This may sound silly, but it gives much faster feedback to the
     * user if used properly: Whenever the user clicks "Next" or
     * "Back", call ClearContents() prior to any lengthy
     * operation -> the user notices instant response, even though he
     * may in fact still have to wait.
     **/
    global void ClearContents() {
	Wizard::SetContents("", `Empty(), "", false, false);
    }


    /**
     * Set contents and Buttons of wizard dialog
     *
     * Additionally set its title, help_text and buttons labels. Enables both back and next button.
     * @param title title of window
     * @param contents contents of dialog
     * @param help_text help text
     * @param back_label label of back button
     * @param next_label label of next button
     **/
    global void SetContentsButtons (string title,
            term contents,
            string help_text,
            string back_label,
            string next_label)
    {
	UI::PostponeShortcutCheck ();

	if ( UI::WizardCommand(`SetBackButtonLabel( back_label ) ) == true )
	{
	    UI::WizardCommand(`SetNextButtonLabel( next_label ) );
	    Wizard::SetContents( title, contents, help_text, true, true );
	}
	else
	{
	    // Set button labels first to avoid geometry problems: SetContents()
	    // calls ReplaceWidget() wich triggers a re-layout.

	    if ( UI::WidgetExists(`id(`back ) ) )	UI::ChangeWidget(`id (`back), `Label, back_label);
	    if ( UI::WidgetExists(`id(`next ) ) )	UI::ChangeWidget(`id (`next), `Label, next_label);
	    Wizard::SetContents( title, contents, help_text, true, true );
	}

	UI::CheckShortcuts ();
    }



    /**
     * Replace the wizard help subwindow with a custom widget.
     *
     * @deprecated
     * @param contents Replace Help with contents
     **/
    global void ReplaceHelp(term contents) {
	if ( UI::WidgetExists( `id(`helpSpace ) ) )
	{
	    y2warning( "Wizard::ReplaceHelp() is deprecated!" );
	    UI::ReplaceWidget(`id(`helpSpace), contents);
	}
	else
	{
	    y2error( "Wizard::ReplaceHelp() is not supported by the new Qt wizard!" );
	}
    }



    /**
     * Set a new help text.
     * @param help_text Help text
     * @example Wizard::SetHelpText("This is a help Text");
     **/
    global void SetHelpText(string help_text)
    {
	if ( UI::WizardCommand(`SetHelpText( help_text ) ) == false )
	{
	    if ( UI::WidgetExists( `id(`helpSpace ) ) )
		UI::ReplaceWidget(`id(`helpSpace), `RichText(`id(`HelpText), help_text ) );
	}
    }


    /**
     * Restore the wizard help subwindow.
     * @param help_text Help text
     **/
    global void RestoreHelp(string help_text)
    {
	Wizard::SetHelpText( help_text );
    }


    /**
     * Set the dialog's "Next" button with a new label and a new ID
     * @param id Button ID
     * @param label Button Label
     **/
    global void SetNextButton( any id, string label )
    {
	if ( UI::WizardCommand(`SetNextButtonLabel( label ) ) == true )
	{
	    UI::WizardCommand(`SetNextButtonID( id ) );
	}
	else
	{
	    if ( UI::WidgetExists(`id(`rep_next ) ) )
	    {
		UI::ReplaceWidget(`id(`rep_next ),
				  `PushButton( `id( id ), `opt (`key_F10, `default), label ) 
				  );
	    }
	}

    }


    /**
     * Set the dialog's "Back" button with a new label and a new ID
     * @param id Button ID
     * @param label Button Label
     **/
    global void SetBackButton( any id, string label )
    {
	if ( UI::WizardCommand(`SetBackButtonLabel( label ) ) == true )
	{
	    UI::WizardCommand(`SetBackButtonID( id ) );
	}
	else
	{
	    if ( UI::WidgetExists(`id(`rep_back ) ) )
	    {
		UI::ReplaceWidget(`id(`rep_back),
				  `PushButton( `id( id ), `opt (`key_F8), label )
				  );
	    }
	}
    }


    /**
     * Set the dialog's "Abort" button with a new label and a new ID
     * @param id Button ID
     * @param label Button Label
     **/
    global void SetAbortButton( any id, string label )
    {
	if ( UI::WizardCommand(`SetAbortButtonLabel( label ) ) == true )
	{
	    UI::WizardCommand(`SetAbortButtonID( id ) );
	}
	else
	{
	    if ( UI::WidgetExists(`id(`rep_abort ) ) )
	    {
		UI::ReplaceWidget(`id(`rep_abort ),
				  `PushButton( `id( id ), `opt (`key_F9), label )
				  );
	    }
	}
    }
    /**
     * Hide the Wizard's "Next" button.
     * Restore it later with RestoreNextButton():
     **/
    global void HideNextButton()
    {
	if ( UI::WizardCommand(`SetNextButtonLabel( "" ) ) == false )
	{
	    if ( UI::WidgetExists(`id(`rep_next ) ) )
		UI::ReplaceWidget(`id(`rep_next ), `Empty() );
	}
    }

    /**
     * Hide the Wizard's "Back" button.
     * Restore it later with RestoreBackButton():
     **/
    global void HideBackButton()
    {
	if ( UI::WizardCommand(`SetBackButtonLabel( "" ) ) == false )
	{
	    if ( UI::WidgetExists(`id(`rep_back ) ) )
		UI::ReplaceWidget(`id(`rep_back), `Empty() );
	}
    }


    /**
     * Hide the Wizard's "Abort" button.
     * Restore it later with RestoreAbortButton():
     **/
    global void HideAbortButton()
    {
	if ( UI::WizardCommand(`SetAbortButtonLabel( "" ) ) == false )
	{
	    if ( UI::WidgetExists(`id(`rep_abort ) ) )
		UI::ReplaceWidget(`id(`rep_abort ), `Empty() );
	}
    }


    /**
     * Restore the wizard 'back' button.
     **/
    global void RestoreBackButton()
    {
	Wizard::SetBackButton( `back, Label::BackButton() );
    }


    /**
     * Restore the wizard 'next' button.
     **/
    global void RestoreNextButton()
    {
	Wizard::SetNextButton( `next, Label::NextButton() );
    }


    /**
     * Restore the wizard 'abort' button.
     **/
    global void RestoreAbortButton() {
	Wizard::SetAbortButton( `abort, Label::AbortButton() );
    }



    /**
     * Replace the wizard 'next' button with a custom widget.
     * THIS FUNCTION IS DEPRECATED!
     *
     * @param contents a term describing the new contents
     **/
    global void ReplaceNextButton(term contents) {
	if ( UI::WidgetExists(`id(`rep_next ) ) )
	    UI::ReplaceWidget(`id(`rep_next), contents);

	y2warning( "Wizard::ReplaceNextButton() is deprecated!" );
    }

    /**
     * Replace the wizard 'back' button with a custom widget.
     * THIS FUNCTION IS DEPRECATED!
     * @param contents a term describing the new contents
     **/
    global void ReplaceBackButton(term contents) {
	if ( UI::WidgetExists(`id(`rep_back ) ) )
	    UI::ReplaceWidget(`id(`rep_back), contents);

	y2warning( "Wizard::ReplaceBackButton() is deprecated!" );
    }


    /**
     * Replace the wizard 'abort' button with a custom widget.
     * THIS FUNCTION IS DEPRECATED!
     * @param contents a term describing the new contents
     **/
    global void ReplaceAbortButton(term contents) {
	if ( UI::WidgetExists(`id(`rep_abort ) ) )
	    UI::ReplaceWidget(`id(`rep_abort), contents);

	y2warning( "Wizard::ReplaceAbortButton() is deprecated!" );
    }



    /**
     * Set the wizard 'title' icon
     *
     * Set the wizard 'title' icon to the specified icon from the standard icon
     * directory.
     *
     * @param icon_name name (without path) of the new icon
     **/
    global void SetTitleIcon( string icon_name )
    {
	string icon = icon_path + "/" + icon_name + ".png";

	// UI::WizardCommand(`SetVerboseCommands( true ) );
	UI::WizardCommand(`SetDialogIcon( icon ) );
	// UI::WizardCommand(`SetVerboseCommands( false ) );
    }


    /**
     * Clear the wizard 'title' icon, i.e. replace it with nothing
     **/
    global void ClearTitleIcon()
    {
	UI::WizardCommand(`SetDialogIcon( "" ) );
    }


    /**
     * Set Desktop Icon
     * @param file Icon name
     * @return boolean true on success
     */
    global boolean SetDesktopIcon( string file )
    {
	string filename = sformat("%1/%2.desktop", Directory::desktopdir, file);
	path filepath = .yast2.desktop.v + filename + ."Desktop Entry".Icon;
	string icon = (string) SCR::Read(filepath);
	y2debug("icon: %1 (%2)", icon, filepath);

	if (icon == nil) 
	    return false;

	SetTitleIcon(icon);
	return true;
    }

    /**
     * PRIVATE - Replace the entire Wizard button box with a new one.
     * @param button_box Button Box term
     * @return void
     **/
    void ReplaceButtonBox( term button_box )
    {
	UI::ReplaceWidget(`id(`rep_button_box ), button_box );
    }



    /**
     * Enable the wizard's "Abort" button.
     **/
    global void EnableAbortButton()
    {
	if ( UI::WizardCommand(`EnableAbortButton( true ) ) == false )
		UI::ChangeWidget(`id(`abort), `Enabled, true );
    }


    /**
     * Disable the wizard's "Abort" button.
     **/
    global void DisableAbortButton()
    {
	if ( UI::WizardCommand(`EnableAbortButton( false ) ) == false )
	    UI::ChangeWidget(`id(`abort), `Enabled, false );
    }


    /**
     * Disable the wizard's "Next" (or "Accept") button.
     **/
    global void DisableNextButton()
    {
	if ( UI::WizardCommand(`EnableNextButton( false ) ) == false )
	{
	    if ( UI::WidgetExists(`id(`next ) ) )
		UI::ChangeWidget(`id(`next), `Enabled, false );
	    else
		UI::ChangeWidget(`id(`accept), `Enabled, false );
	}
    }


    /**
     * Enable the wizard's "Next" (or "Accept") button.
     **/
    global void EnableNextButton()
    {
	if ( UI::WizardCommand(`EnableNextButton( true ) ) == false )
	{
	    if ( UI::WidgetExists(`id(`next ) ) )
		UI::ChangeWidget(`id(`next), `Enabled, true );
	    else
		UI::ChangeWidget(`id(`accept), `Enabled, true );
	}
    }


    /**
     * Disable the wizard's "Back" button.
     **/
    global void DisableBackButton()
    {
	if ( UI::WizardCommand(`EnableBackButton( false ) ) == false )
	    UI::ChangeWidget(`id(`back ), `Enabled, false );
    }

    /**
     * Enable the wizard's "Back" button.
     **/
    global void EnableBackButton()
    {
	if ( UI::WizardCommand(`EnableBackButton( true ) ) == false )
	    UI::ChangeWidget(`id(`back ), `Enabled, true );
    }


    /**
     * Disable the wizard's "Cancel" button.
     **/
    global void DisableCancelButton()
    {
	if ( UI::WizardCommand(`EnableCancelButton( false ) ) == false )
	    UI::ChangeWidget(`id(`cancel ), `Enabled, false );
    }

    /**
     * Enable the wizard's "Cancel" button.
     **/
    global void EnableCancelButton()
    {
	if ( UI::WizardCommand(`EnableCancelButton( true ) ) == false )
	    UI::ChangeWidget(`id(`cancel ), `Enabled, true );
    }

    /**
     * Show a "Release Notes" button with the specified label and ID if there is a "steps" panel
     **/
    global void ShowReleaseNotesButton( string label, any id )
    {
	if ( UI::WizardCommand(`ShowReleaseNotesButton( label, id ) ) == false )
	{
	    UI::ReplaceWidget (`id (`back_rep), `PushButton (`id (id), label));
	}
    }


    /**
     * Hide the "Release Notes" button, if there is any
     **/
    global void HideReleaseNotesButton()
    {
	if ( UI::WizardCommand(`HideReleaseNotesButton() ) == false )
	{
	    UI::ReplaceWidget (`id (`back_rep), `Empty ());
	}
    }


    /**
     * Retranslate the wizard buttons.
     *
     * This will revert button labels and IDs
     * to the default that were used upon Wizard::CreateDialog(),
     * Wizard::OpenNextBackDialog(), or Wizard::OpenAcceptDialog().
     **/
    global void RetranslateButtons()
    {
	if ( UI::WidgetExists(`id(`WizardDialog ) ) == true )
	{
	    ReplaceButtonBox( UI::WidgetExists(`id(`accept ) ) ?
			      Wizard::AbortAcceptButtonBox() :
			      Wizard::BackAbortNextButtonBox() );
	}
	else // Qt wizard
	{
	    UI::WizardCommand(`RetranslateInternalButtons() );
	    
	    if ( UI::WidgetExists(`accept) )
	    {
		UI::WizardCommand(`SetBackButtonLabel ( "" ) );
		UI::WizardCommand(`SetAbortButtonLabel( Label::AbortButton()  ) );
		UI::WizardCommand(`SetNextButtonLabel ( Label::AcceptButton() ) );
	    }
	    else
	    {
		UI::WizardCommand(`SetBackButtonLabel ( Label::BackButton()  ) );
		UI::WizardCommand(`SetAbortButtonLabel( Label::AbortButton() ) );
		UI::WizardCommand(`SetNextButtonLabel ( Label::NextButton()  ) );
	    }
	}
    }


    /**
     * Set the keyboard focus to the wizard's "Next" (or "Accept") button.
     **/
    global void SetFocusToNextButton()
    {
	if ( UI::WizardCommand(`SetFocusToNextButton() ) == false )
	{
	    UI::SetFocus( UI::WidgetExists(`id(`next) ) ? `id(`next) : `id(`accept ) );
	}
    }

    /**
     * Set the keyboard focus to the wizard's "Back" (or "Cancel") button.
     **/
    global void SetFocusToBackButton()
    {
	if ( UI::WizardCommand(`SetFocusToBackButton() ) == false )
	{
	    UI::SetFocus( UI::WidgetExists(`id(`back) ) ? `id(`back) : `id(`cancel ) );
	}
    }




    /* ------------------------------------------------------------ */
    /*
     * screenshot functions
     */

    /**
     * Currently used screenshot name.
     * Initially, it must match the UI default, "yast2"
     */
    string screenshot_name = "yast2";
    /**
     * Screenshot names overriden by nested SetScreenShotName calls
     */
    list<string> screenshot_name_stack = [];

    /**
     * Declare a name for the current dialog
     *
     * Declare a name for the current dialog to ease making screenshots.
     * By convention, the name is
     * {rpm-name-without-yast2}-{sorting-prefix}-{description}
     * The calls may be nested.
     * @param s eg. "mail-1-conntype"
     * @see RestoreScreenShotName
     */
    global void SetScreenShotName (string s) {
	screenshot_name_stack = prepend (screenshot_name_stack, screenshot_name);
	screenshot_name = s;
	UI::SetModulename (screenshot_name);
    }

    /**
     * Restore the screenshot name.
     *
     * If it does not match a SetScreenShotName, "yast2" is used
     * and a y2error logged.
     */
    global void RestoreScreenShotName () {
	screenshot_name = screenshot_name_stack[0]:nil;
	if (screenshot_name == nil)
	{
	    screenshot_name = "yast2";
	    y2error (1, "No screenshot name to restore!");
	}
	else
	{
	    screenshot_name_stack = remove (screenshot_name_stack, 0);
	}
	UI::SetModulename (screenshot_name);
    }

    /* ------------------------------------------------------------ */
    /*
     * Tree & Menu Wizard functions
     */

    /**
     * Open a Tree  dialog with buttons "Next", "Back", "Abort"
     * and set the keyboard focus to "Next".
     **/
    global void OpenTreeNextBackDialog()
    {
        if ( haveFancyUI() )
        {
            UI::OpenDialog(`opt(`defaultsize ),
                    `Wizard(`opt(`treeEnabled),
                        `back, Label::BackButton(),
                        `abort, Label::AbortButton(),
                        `next, Label::NextButton()
                        )
                    );
        }
        else
        {
	    Wizard::OpenDialog( NextBackDialog() );
	    UI::SetFocus(`id(`next) );
        }
    }

    /**
     * Create and open a Tree wizard dialog.
     *
     * For backwards compatibility only - don't use this any more in new modules.
     **/
    global void CreateTreeDialog()
    {
	Wizard::OpenTreeNextBackDialog();
        return;
    }

    /**
     * Add Tree Item to tree enabled Wizard
     * @param Tree Tree Data
     * @param parent Parent of this item
     * @param title Item Title
     * @param id Item ID
     * @return list<map> Updated Tree Data
     **/
    global list<map> AddTreeItem(list<map> Tree, string parent , string title, string id) {
        if ( haveFancyUI() )
        {
            UI::WizardCommand(`AddTreeItem( parent, title , id ) );
        }
        else
        {
            Tree = add(Tree, $[ "parent": parent, "title":title, "id": id]);
        }
        return Tree;
    }


    /**
     * Create the Tree Items
     * @param Tree Tree data
     * @param parent Parent of current Item
     * @return list Tree Items
     **/
    list CreateTreeInternal(list<map> Tree, string parent) {
        list<map> m = filter(map c, Tree, ``((c["parent"]:"" == parent )));
        map ccbak = nil; // #38596, broken recursion for iterators
        list mm = maplist(map cc, m, ``{
                    string TreeEntry = cc["id"]:"";
                    ccbak = cc;
                    list items = CreateTreeInternal(Tree, TreeEntry);
                    cc = ccbak;
                    if (size(items)>0)
                        return(`item(`id(cc["id"]:""), cc["title"]:"", items));
                    else
                        return(`item(`id(cc["id"]:""), cc["title"]:""));
                });
        y2debug("items: %1", mm );
        return mm;
    }


    /**
     * Query Tree Item
     * @return Tree Item
     */
    global string QueryTreeItem() {

        if (haveFancyUI() )
        {
            return (string)UI::QueryWidget(`id(`wizard), `CurrentItem );
        }
        else
        {
            return (string)UI::QueryWidget(`id(`wizardTree), `CurrentItem );
        }
    }

    /**
     * Create the tree in the dialog, replaces helpspace with new tree widget
     * @param Tree Tree data
     * @param title Tree title
     **/
    global void CreateTree(list<map> Tree, string title) {
        if ( !haveFancyUI() )
        {
            list items = [];
            foreach(map i, Tree, ``{
                    if (i["parent"]:"" == "")
                    {
                        items=add(items, `item(`id(i["id"]:""), i["title"]:"",
                                CreateTreeInternal(Tree, i["id"]:"") ));
                    }
                    });
            y2debug("tree items: %1", items);
            Wizard::ReplaceCustomHelp(
                    `VBox(
                        `Tree(
                            `id(`wizardTree),
                            `opt(`notify, `vstretch),
                            title,
                            items
                            ),
                        `VSpacing(1))
                    );
        }
        return;

    }

    /**
     * Select Tree item
     * @param tree_item tree item
     */
    global void SelectTreeItem(string tree_item) {
        if ( haveFancyUI() )
        {
            UI::WizardCommand(`SelectTreeItem( tree_item ) );
        }
        else
        {
            UI::ChangeWidget(`id(`wizardTree), `CurrentItem, tree_item);
        }
    }
    /**
     * Delete Tree items
     */
    global void DeleteTreeItems() {
        if ( haveFancyUI() )
        {
            UI::WizardCommand(`DeleteTreeItems() );
        }
        else
        {
            Wizard::ReplaceCustomHelp(`Empty());
        }
    }

    /**
     * Delete Menu items
     */
    global void DeleteMenus() {
        if ( haveFancyUI() )
        {
            UI::WizardCommand(`DeleteMenus() );
        }
        else
        {
            UI::ReplaceWidget(`id(`topmenu), `Empty());
        }
    }
    /**
     * Add Menu
     * @param Menu  Menu data
     * @param title Menu Title
     * @param id Menu ID
     * @return list<map> Updated Menu Data
     **/
    global list<map> AddMenu(list<map> Menu, string title, string id) {
        if ( haveFancyUI() )
        {
            UI::WizardCommand(`AddMenu( title, id ) );
        }
        else
        {
            Menu = add(Menu, $["type":"Menu", "title":title, "id": id]);
        }
        return Menu;
    }

    /**
     * Add Sub Menu
     * @param Menu Menu data
     * @param parent_id Menu Parent
     * @param title Menu Title
     * @param id Menu ID
     * @return list<map> Updated Menu Data
     **/
    global list<map>  AddSubMenu(list<map> Menu, string parent_id, string title, string id) {
        if ( haveFancyUI() )
        {
            UI::WizardCommand(`AddSubMenu( parent_id, title, id ) );
        }
        else
        {
            Menu = add(Menu, $["type":"SubMenu", "parent": parent_id, "title":title, "id": id]);
        }
        return Menu;
    }

    /**
     * Add Menu Entry
     * @param Menu Menu data
     * @param parent_id Menu Parent
     * @param title Menu Title
     * @param id Menu ID
     * @return list<map> Updated Menu Data
     **/
    global list<map> AddMenuEntry(list<map> Menu, string parent_id, string title, string id) {
        if ( haveFancyUI() )
        {
            UI::WizardCommand(`AddMenuEntry( parent_id, title, id ) );
        }
        else
        {
            Menu = add(Menu, $["type":"MenuEntry", "parent": parent_id, "title":title, "id": id]);
        }
        return Menu;
    }

    /**
     * Create the Menu Items
     * @param Menu Menu data
     * @param parent Menu Parent
     * @return list Menu Items
     **/
    list CreateMenuInternal(list<map> Menu, string parent) {
        list<map> m = filter(map c, Menu, ``((c["parent"]:"" == parent )));

        list mm = maplist(map cc, m, ``{
                if(cc["type"]:"" == "MenuEntry")
                {
                    string MenuEntry = cc["id"]:"";
                    return(`item(`id(MenuEntry), cc["title"]:""));
                }
                else if (cc["type"]:"" == "SubMenu")
                {
                    string SubMenu = cc["id"]:"";
                    return(`menu( cc["title"]:"", CreateMenuInternal(Menu,
                                SubMenu)));
                }
                });
        y2debug("items: %1", mm );
        return mm;
    }

    /**
     * Create the menu in the dialog
     * @param Menu Menu data
     * @return void
     **/
    global void CreateMenu(list<map> Menu) {
        if (!haveFancyUI() )
        {
            term menu_term = `HBox();
            foreach(map m, Menu, ``{
                    if (m["type"]:"" == "Menu")
                    {
                        list menu_items = CreateMenuInternal(Menu, m["id"]:"");
                        y2debug("menu_items: %1", menu_items );
                        menu_term=add(menu_term, `MenuButton( m["title"]:"",
                                menu_items));
                    }
                    });
            y2milestone("menu: %1", menu_term );
            UI::ReplaceWidget(`id(`topmenu), `Left(menu_term));
        }
        return;
    }


    /**
     * Set the product name for UI
     * @param name the product name
     * @return void
     **/
    global void SetProductName (string name) {
        y2milestone ("Setting product name to '%1'", name);
        product_name = name;
    }


/* EOF */
}
