/**
 * File:	modules/Popup.ycp
 * Package:	yast2
 * Summary:	Commonly used popup dialogs
 * Authors:	Gabriele Strattner <gs@suse.de>
 *		Stefan Hundhammer <sh@suse.de>
 *		Arvin Schnell <arvin@suse.de>
 *
 * $Id: Popup.ycp 21421 2005-02-08 16:03:37Z sh $
 *
 * Contains commonly used popup dialogs
 * for general usage, e.g. Popup::YesNo(), Popup::ContinueCancel().
 * <br>
 * See also <a href="../wizard/README.popups">README.popups</a>
 */

{

module "Popup";

textdomain "base";

import "Label";
import "Mode";
import "Directory";

boolean feedback_open = false;


/**
 * Internal function that returns a popup dialog layout.
 *
 * @param headline	headline to show or Popup::NoHeadline()
 * @param message	message text to show
 * @param icon_name	icon name (with full path) or Popup::NoIcon()
 * @param button_box	term with one or more buttons
 *
 * @return the layout contents as a term
 */
define term popupLayoutInternal( string headline, string message, string icon_name, term button_box )
{
    term content = `Empty();
    term icon    = `Empty();

    if ( size( icon_name ) > 0 )
    {
	map ui_capabilities = UI::GetDisplayInfo();

	if ( ui_capabilities[ "HasLocalImageSupport" ]:false )
	{
	    icon = `Image( icon_name, "" );
	}
    }

    if ( size( headline ) > 0 )
    {
	content =
	    `VBox(
		  `HBox(
			`VCenter( icon ),
			`HSpacing(1),
			`VCenter(`Heading( headline ) ),
			`HStretch()
			),
		  `VSpacing(0.2),
		  `Left(`Label( message ) )
		  );
    }
    else // no headline
    {
	content =
	    `HBox(
		  `Top( icon ),
		  `HSpacing(1),
		  `VCenter(`Label( message ) )
		  );
    }

    term dialog =
	`HBox(
	      `HSpacing(1),
	      `VBox(
		    `VSpacing(0.2),
		    `HSquash( content ),
		    `VStretch(),
		    `HSquash( button_box ),
		    `VStretch(),
		    `VSpacing(0.2)
		    ),
	      `HSpacing(1)
	      );

    return dialog;
}


/**
 * Internal function that returns a popup dialog with an additional label.
 *
 * @param headline	headline to show or Popup::NoHeadline()
 * @param message	message text to show
 * @param icon_name	icon name (with full path) or Popup::NoIcon()
 * @param button_box	term with one or more buttons
 * @param label		second label with id `label which can be used e.g. for time out value displaying
 *
 * @return term the layout contents as a term
 */
define term popupLayoutInternalWithLabel( string headline,
					  string message,
					  string icon_name,
					  term button_box,
					  string label )
{
    term content = `Empty();
    term heading = `VSpacing(0.2);
    term icon    = `Empty();

    if ( size( icon_name ) > 0 )
    {
	map ui_capabilities = UI::GetDisplayInfo();

	if ( ui_capabilities[ "HasLocalImageSupport" ]:false )
	{
	    icon = `Image( icon_name, "" );
	}
    }

    if ( size( headline ) > 0 )
    {
	content =
	    `VBox(
		  `HBox(
			`VCenter( icon ),
			`HSpacing(1),
			`VCenter(`Heading( headline ) ),
			`HStretch()
			),
		  `VSpacing(0.2),
		  `Left(`Label( message ) ),
		  `VSpacing(0.2),
		  `Label(`id(`label), label )
		  );
    }
    else // no headline
    {
	content =
	    `VBox(
		  `HBox(
			`Top( icon ),
			`HSpacing(1),
			`VCenter(`Label( message ) )
			),
		  `VSpacing(0.2),
		  `Label(`id(`label), label )
		  );
    }

    term dialog =
	`HBox(
	      `HSpacing(1),
	      `VBox(
		    `VSpacing(0.2),
		    `HSquash( content ),
		    `VStretch(),
		    `HSquash( button_box ),
		    `VStretch(),
		    `VSpacing(0.2)
		    ),
	      `HSpacing(1)
	      );

    return dialog;
}


/**
 * Returns the full icon path for a icon with the specified base name.
 * "warning.png" -> "/usr/lib/YaST2/theme/icons/32x32/apps/warning.png"
 **/
define string popupIcon( string icon_base_name )
{
    return Directory::icondir + "32x32/apps/" + icon_base_name;
}


/**
 * Indicator for empty headline for popups that can optionally have one
 *
 * This is really just an alias for the empty string "", but it is
 * slightly better readable.
 *
 * @return empty string ("")
 */
global define string NoHeadline()
{
    return "";
}


/**
 * Indicator for empty icon for popups that can have one - for code readability.
 **/
global define string NoIcon()
{
    return "";
}

/**
 * Button box for the AnyQuestion Dialog (internal function).
 *
 * @param yes_button_message	label on affirmative buttons (on left side)
 * @param no_button_message	label on negating button (on right side)
 * @param focus			`focus_yes (first button) or `focus_no (second button)
 *
 * @return term button box
 */
define term AnyQuestionButtonBox (
	string yes_button_message,
	string no_button_message,
	symbol focus )
{
    term yes_button = `Empty();
    term no_button = `Empty();

    if ( focus == `focus_no )
    {
	yes_button = `PushButton( `id(`yes),
				  `opt (`key_F10),
				  yes_button_message );
	no_button  = `PushButton( `id(`no_button),
				  `opt(`default, `key_F9),
				  no_button_message );
    }
    else
    {
	yes_button = `PushButton( `id(`yes),
				  `opt(`default, `key_F10),
				  yes_button_message);
	no_button  = `PushButton( `id(`no_button),
				  `opt (`key_F9),
				  no_button_message );
    }

    term button_box =  `HBox(
			     `HWeight( 1, yes_button),
			     `HSpacing(2),
			     `HWeight( 1, no_button )
			     );
    return button_box;
}


/**
 * Generic question popup with two buttons.
 *
 * Style guide hint: The first button has to have the semantics of "yes",
 * "OK", "continue" etc., the second its opposite ("no", "cancel", ...).
 * NEVER use this generic question popup to simply exchange the order of
 * yes/no, continue/cancel or ok/cancel buttons!
 *
 * @param headline		headline or Popup::NoHeadline()
 * @param message		message string
 * @param yes_button_message	label on affirmative buttons (on left side)
 * @param no_button_message	label on negating button (on right side)
 * @param focus			`focus_yes (first button) or `focus_no (second button)
 * @screenshot screenshots/AnyQuestion.png
 *
 * @return true:  first button has been clicked
 *	 false: second button has been clicked
 *
 * @see YesNo
 * @see ContinueCancel
 *
 * @example Popup::AnyQuestion( Label::WarningMsg(), "Do really want to ...?", "Install", "Don't do it", `focus_no );
 */
global define boolean AnyQuestion( string headline,
				   string message,
				   string yes_button_message,
				   string no_button_message,
				   symbol focus )
{
    term button_box = AnyQuestionButtonBox ( yes_button_message, no_button_message, focus);
    UI::OpenDialog( 
		   `opt(`decorated),
		   popupLayoutInternal( headline, message, NoIcon(), button_box )
		   );

    any ret = UI::UserInput();
    UI::CloseDialog();

    return ret == `yes;
}


/**
 * Timed question popup with two buttons and time display
 *
 * @param headline		headline or Popup::NoHeadline()
 * @param message		message string
 * @param yes_button_message	label on affirmative buttons (on left side)
 * @param no_button_message	label on negating button (on right side)
 * @param focus			`focus_yes (first button) or `focus_no (second button)
 * @param timeout_seconds	timeout, if 0, normal behaviour
 * @return boolean              True if Yes, False if no
 * @see AnyQuestion
 */
global define boolean TimedAnyQuestion( string headline,
				   string message,
				   string yes_button_message,
				   string no_button_message,
				   symbol focus ,
				   integer timeout_seconds)
{
    term button_box = AnyQuestionButtonBox ( yes_button_message, no_button_message, focus);
    term timed =
	`ReplacePoint(`id(`replace_buttons) ,
		      `VBox(
			    `HCenter(
				     `Label(`id(`remaining_time), "" + timeout_seconds)
				     ),
			    `HBox(
				  `PushButton(`id(`timed_stop), Label::StopButton() ),
				  `HSpacing(2),
				  `PushButton(`id(`timed_ok), `opt(`default, `key_F10), Label::OKButton() )
				  ),
			    `VSpacing(0.2)
			    )
		      );


    UI::OpenDialog(
		   `opt(`decorated),
		   popupLayoutInternal( headline, message, NoIcon(), timed )
		   );

    any which_input = nil;

    while (timeout_seconds > 0) {
	which_input = UI::TimeoutUserInput( 1000 );
	if (which_input == `timed_ok)
	    break;
	if (which_input == `timed_stop)
	{
	    UI::ReplaceWidget(`id(`replace_buttons), button_box);
	    while (which_input == `timed_stop)
		which_input = UI::UserInput();
	    break;
	}
	timeout_seconds = timeout_seconds - 1;
	UI::ChangeWidget (`id(`remaining_time), `Value, ""+timeout_seconds);
    }
    UI::CloseDialog();

    return which_input == `yes;
}


/**
 * Dialog which displays the "message" and has a <b>Continue</b>
 * and a <b>Cancel</b> button.
 *
 * This popup should be used to confirm possibly dangerous actions and if
 * it's useful to display a short headline (without headline
 * Popup::ContinueCancel() can be used).
 * The default button is Continue.
 *
 * Returns true if Continue is clicked.
 *
 * @screenshot screenshot/ContinueCancelHeadline.png
 *
 * @param  headline short headline or Popup::NoHeadline()
 * @param  message  message string
 * @return boolean
 *
 * @example Popup::ContinueCancelHeadline ( "Short Header", "Going on with action....?" );
 *
 * @see	ContinueCancel
 * @see	YesNo
 * @see	AnyQuestion
 */
global define boolean ContinueCancelHeadline (string headline, string message)
{
    boolean ret = AnyQuestion( headline,
			       message,
			       Label::ContinueButton(),
			       Label::CancelButton(),
			       `focus_yes
			       );

    y2debug("ContinueCancelHeadline returned: %1", ret );

    return ret;
}


/**
 * Dialog which displays the "message" and has a <b>Continue</b>
 * and a <b>Cancel</b> button.
 *
 * This popup should be used to confirm possibly dangerous actions.
 * The default button is Continue.
 * Returns true if Continue is clicked.
 *
 * @screenshot screenshots/ContinueCancel.png
 *
 * @param  message  message string
 * @return boolean
 *
 * @example Popup::ContinueCancel ( "Please insert required CD-ROM." );
 *
 * @see	AnyQuestion
 */
global define boolean ContinueCancel (string message)
{

    boolean ret = ContinueCancelHeadline( NoHeadline(), message );
    y2debug("ContinueCancel returned: %1", ret );

    return ret;
}


/**
 * This dialog displays "message" (a question) and has a <b>Yes</b> and
 * a <b>No</b> button.
 *
 * It should be used for decisions about two about equivalent paths,
 * not for simple confirmation - use "Popup::ContinueCancel()" for those.
 * A short headline can be displayed (without headline you can use Popup::YesNo()).
 *
 * The default button is Yes.
 *
 * Returns true if <b>Yes</b> is clicked.
 *
 * @screenshot screenshots/YesNoHeadline.png
 *
 * @param  headline	short headline or Popup::NoHeadline()
 * @param  message	message string
 * @return boolean
 *
 * @example  Popup::YesNoHeadline ( "Resize Windows Partition?", "... explanation of dangers ..." );
 *
 * @see	YesNo
 * @see	AnyQuestion
 */
global define boolean YesNoHeadline(string headline, string message)
{

    boolean ret = AnyQuestion( headline,
			       message,
			       Label::YesButton(),
			       Label::NoButton(),
			       `focus_yes
			       );

    y2debug("YesNoHeadline returned: %1", ret );

    return ret;
}


/**
 * Display a yes/no question and wait for answer.
 *
 * Should be used for decisions about two about equivalent paths,
 * not for simple confirmation - use "Popup::ContinueCancel()" for those.
 * The default button is Yes.
 * Returns true if <b>Yes</b> is clicked.
 *
 * @screenshot screenshots/YesNo.png
 *
 * @param  message	message string
 * @return boolean
 *
 * @example  Popup::YesNo ( "Create a backup of the config files?" );
 *
 * @see	YesNoHeadline
 * @see	ContinueCancel
 * @see	AnyQuestion
 */
global define boolean YesNo(string message)
{
    boolean ret = YesNoHeadline( NoHeadline(), message);

    y2debug("YesNo returned: %1", ret );

    return ret;
}


/**
 * Show a long text that might need scrolling.
 *
 * Pass a RichText widget with the parameters appropriate for your text -
 * i.e. without special parameters for HTML-like text or with
 * `opt(`plainText) for plain ASCII text without HTML tags.
 *
 * @screenshot screenshots/LongText.png
 *
 * @param  headline short headline
 * @param  richtext  text input is `Richtext()
 * @param  hdim  initial horizontal dimension of the popup
 * @param  vdim  initial vertical dimension of the popup
 *
 * @example Popup::LongText ( "Package description", `Richtext("<p>Hello, this is a long description .....</p>"), 50, 20 );
 */
global define void LongText( string headline, term richtext,
			     integer hdim, integer vdim )
{
    UI::OpenDialog( `opt( `decorated ),
		    `HBox( `VSpacing(vdim),
			   `VBox (`HSpacing(hdim),
				  `Left(`Heading( headline )),
				  `VSpacing(0.2),
				  richtext,	// scrolled text
				  `PushButton( `id(`ok), `opt(`default, `key_F10), Label::OKButton() )
				  )
			   )
		    );

    UI::SetFocus(`id(`ok ) );

    UI::UserInput();
    UI::CloseDialog();
}


/**
 * Show a question that might need scrolling.
 *
 * @param  headline short headline
 * @param  richtext  text input as a rich text
 * @param  hdim  initial horizontal dimension of the popup
 * @param  vdim  initial vertical dimension of the popup
 * @param  yes_button_message message on the left/true button
 * @param  no_button_message message on the right/false button
 * @param  focus `focus_yes, `focus_no, `focus_none
 * @return left button pressed?
 */
global define boolean AnyQuestionRichText (string headline,
					   string richtext,
					   integer hdim, integer vdim,
					   string yes_button_message,
					   string no_button_message,
					   symbol focus)
{
    term yes_button = `PushButton (
	`id (`ok),
	( focus == `focus_yes ?
	  `opt (`default, `key_F10) :
	  `opt (`key_F10) ),
	yes_button_message);

    term no_button = `PushButton (
	`id (`cancel),
	( focus == `focus_no ?
	  `opt (`default, `key_F9) :
	  `opt (`key_F9) ),
	no_button_message);

    term d = `HBox (
	`VSpacing (vdim),
	`VBox (
	    `HSpacing (hdim),
	    size (headline) > 0 ? `Left(`Heading( headline )) : `Empty (),
	    `VSpacing(0.2),
	    `RichText (richtext),
	    `HBox (
		yes_button,
		no_button
		)
	    )
	);
    UI::OpenDialog (`opt (`decorated), d);
    any ui = UI::UserInput ();
    UI::CloseDialog ();
    return ui == `ok;
}


/**
 * Confirmation for "Abort" button during installation.
 *
 * According to the "severity" parameter an appropriate text will be
 * displayed indicating what the user has to expect when he really aborts now.
 *
 * @screenshot screenshots/ConfirmAbort.png
 *
 * @param severity		`painless, `incomplete, `unusable
 *
 * @return boolean
 *
 * @example Popup::ConfirmAbort ( `painless );
 */
global define boolean ConfirmAbort( symbol severity )
{
    string what_will_happen = "";

    // Confirm user request to abort installation
    string abort_label = _("Really abort the installation?");
    // Button that will really abort the installation
    string abort_button = _("&Abort Installation");
    // Button that will continue with the installation
    string continue_button = _("&Continue Installation");

    if ( severity == `painless )
    {
	if (Mode::repair ())
	{
	    // Confirm user request to abort System Repair
	    abort_label = _("Really abort YaST System Repair?");
	    // Button that will really abort the repair
	    abort_button = _("Abort System Repair");
	    // Button that will continue with the repair
	    continue_button = _("&Continue System Repair");
	}
	else
	{
	    // Warning text for aborting an installation before anything is installed
	    what_will_happen = _("If you abort the installation now,
Linux will not be installed.
Your hard disk will remain untouched.");
	}
    }
    else if ( severity == `incomplete )
    {
	// Warning text for aborting an installation during the install process
	// - After some installation steps have been performed - e.g.
	// disks formatted / some packages already installed
	what_will_happen = _("If you abort the installation now, you will
have an incomplete Linux system
that might or might not be usable.
You might need to reinstall.
");
    }
    else if ( severity == `unusable )
    {
	// Warning text for aborting an installation during the install process
	// right in the middle of some critical process (e.g. formatting)
	what_will_happen = _("If you abort the installation now,
Linux will be unusable.
You will need to reinstall.");
    }
    else
    {
	y2error ("Unknown symbol for ConfirmAbort");
    }

    string message = abort_label + "\n\n" + what_will_happen;

    boolean ret = AnyQuestion (NoHeadline (), message, abort_button,
			   continue_button, `focus_no);

    y2debug("ConfirmAbort returned: %1", ret);

    return ret;
}


/**
 * Confirmation popup when user clicked "Abort".
 *
 * Set "have changes" to "true" when there are changes that will be lost.
 * Note: If there are none, it is good policy to ask for confirmation
 * anyway, but of course with "have_changes" set to "false" so the user
 * isn't warned about changes that might be lost.
 *
 * @param have_changes	true:  There are changes that will be lost
 *			false: No changes
 *
 * @return	true: "abort" confirmed;
 *		false: don't abort
 */
global define boolean ReallyAbort( boolean have_changes )
{
    symbol focus = `focus_yes;

    // Confirm aborting the program
    string message = _("Really abort?");

    if ( have_changes )
    {
	focus = `focus_no;

	// Additional hint when trying to abort program in spite of changes
	message = message + "\n" + _("All changes will be lost!");
    }

    boolean ret = AnyQuestion( NoHeadline(),
			   message,
			   Label::YesButton(),
			   Label::NoButton(),
			   `focus_no );

    y2debug("ReallyAbort returned: %1", ret );

    return ret;
}


/**
 * Show a simple message and wait until user clicked "OK".
 *
 *
 * @param message message string
 *
 * @example  Popup::Message("This is an information about ... ." );
 *
 * @screenshot screenshots/Message.png
 * @see AnyMessage
 * @see Notify
 * @see Warning
 * @see Error
 */
global define void Message(string message)
{
    UI::OpenDialog(
		   `opt(`decorated),
		   `HBox(
			 `HSpacing(1),
			 `VBox(
			       `VSpacing(0.2),
			       `Label(message),
			       `PushButton(`id(`ok), `opt(`default, `key_F10), Label::OKButton()),
			       `VSpacing(0.2)
			       ),
			 `HSpacing(1)
			 )
		   );
    UI::SetFocus(`id(`ok));
    UI::UserInput();

    UI::CloseDialog();
}


/**
 * Generic message popup - internal
 *
 * Show a message with optional headline above and
 * wait until user clicked "OK".
 *
 * @param headline	optional headline or Popup::NoHeadline()
 * @param message	the message (maybe multi-line) to display.
 */
define void anyMessageInternal(string headline, string message, string icon_name )
{
    term button_box =  `PushButton( `id(`ok_msg), `opt(`default, `key_F10), Label::OKButton() );
    UI::OpenDialog(
		   `opt(`decorated),
		   popupLayoutInternal( headline, message, icon_name, button_box )
		   );

    UI::SetFocus(`id(`ok_msg) );

    UI::UserInput();
    UI::CloseDialog();
}


/**
 * Generic message popup
 *
 * Show a message with optional headline above and
 * wait until user clicked "OK".
 *
 * @param headline	optional headline or Popup::NoHeadline()
 * @param message	the message (maybe multi-line) to display.
 * @param icon_name	icon name (with full path) or Popup::NoIcon()
 */
global define void AnyMessage(string headline, string message)
{
    anyMessageInternal( headline, message, NoIcon() );
}
    

/**
 * Clear feedback message
 * @return void
 */
global define void ClearFeedback()
{
    if ( feedback_open )
	UI::CloseDialog();
    feedback_open = false;
}


/**
 * Show popup with a headline and a message for feedback
 * @param headline headline of Feedback popup
 * @param message the feedback message
 * @return void
 */
global define void ShowFeedback(string headline, string message)
{
    if (feedback_open)
    {
	UI::CloseDialog();
    }
    term button_box =  `Empty();
    UI::BusyCursor();
    UI::OpenDialog(
		   `opt(`decorated),
		   popupLayoutInternal( headline, message, NoIcon(), button_box )
		   );

    feedback_open = true;
}


/**
 * Show a warning message and wait until user clicked "OK".
 *
 *
 * @param message warning message string
 *
 * @example Popup::Warning("Something is wrong. Please check your configuration." );
 *
 * @screenshot screenshots/Warning.png
 * @see Message
 * @see Notify
 * @see Error
 * @see AnyMessage
 */
global define void Warning(string message)
{
    anyMessageInternal( Label::WarningMsg(), message, popupIcon( "msg_warning.png" ) );
}


/**
 * Show an error message and wait until user clicked "OK".
 *
 * @param message	error message string
 *
 * @example  Popup::Error("The configuration was not succesful." );
 * @screenshot screenshots/Error.png
 *
 * @see Message
 * @see Notify
 * @see Warning
 * @see AnyMessage
 */
global define void Error(string message)
{
    anyMessageInternal( Label::ErrorMsg(), message, popupIcon( "msg_error.png" ) );
}


/**
 * Show a notification message and wait until user clicked "OK".
 *
 * @screenshot screenshots/Notify.png
 *
 * @param  message notify message string
 *
 * @example  Popup::Notify("Your printer is ready for use." );
 *
 * @see Message
 * @see AnyMessage
 */
global define void Notify(string message)
{
    anyMessageInternal( "", message, popupIcon( "msg_info.png" ) );
}


/**
 * Display a message with a timeout and return when the user clicks "OK"
 * or when the timeout expires.
 *
 * There is also a "stop" button that will stop the countdown. If the
 * user clicks that, the popup will wait forever (or until "OK" is
 * clicked, of course).
 *
 * @screenshot screenshots/TimedMessage.png
 *
 * @param message		message to display
 * @param timeout_seconds		the timeout in seconds
 *
 * @example Popup::TimedMessage("This is a timed message", 2 );
 */
global define void TimedMessage(string message, integer timeout_seconds)
{
    term button_box =
	`HBox(
	      `HWeight( 1, `PushButton(`id(`timed_stop), Label::StopButton() ) ),
	      `HSpacing(2),
	      `HWeight( 1 , `PushButton(`id(`timed_ok), `opt(`default, `key_F10), Label::OKButton() ) )
	      );
    
    UI::OpenDialog(
		   `opt(`decorated),
		   popupLayoutInternalWithLabel( NoHeadline(), message,
						 popupIcon( "msg_info.png" ),
						 button_box, sformat("%1", timeout_seconds ) )
		   );
    UI::SetFocus(`id(`timed_ok) );

    while (timeout_seconds > 0)
    {
	any which_input = UI::TimeoutUserInput( 1000 );
	if (which_input == `timed_ok)
	    break;
	if (which_input == `timed_stop)
	{
	    while (which_input == `timed_stop)
		which_input = UI::UserInput();
	    break;
	}
	timeout_seconds = timeout_seconds - 1;
	UI::ChangeWidget (`id(`label), `Value, ""+timeout_seconds);
    }
    UI::CloseDialog();
}


/**
 * Display a message with a timeout
 *
 * Display a message with a timeout and return when the user clicks "OK", "Cancel"
 * or when the timeout expires ("OK" is assumed then).
 *
 * There is also a "stop" button that will stop the countdown. If the
 * user clicks that, the popup will wait forever (or until "OK" or "Cancel" is
 * clicked, of course).
 *
 * @param message		message to display
 * @param timeout_seconds		the timeout in seconds
 *
 * @return true	--> "OK" or timer expired<br>
 *	 false  --> "Cancel"
 *
 * @example boolean ret = Popup::TimedOKCancel("This is a timed message", 2 );
 */
global define boolean TimedOKCancel(string message, integer timeout_seconds)
{
    UI::OpenDialog(
		   `opt(`decorated),
		   `HBox(
			 `HSpacing(1),
			 `VBox(
			       `VSpacing(0.2),
			       `Label(message),
			       `HCenter(
					`Label(`id(`remaining_time), "" + timeout_seconds)
					),
			       `HBox(
				     `PushButton(`id(`timed_stop), Label::StopButton() ),
				     `HSpacing(2),
				     `PushButton(`id(`timed_ok), `opt(`default, `key_F10), Label::OKButton() ),
				     `HSpacing(2),
				     `PushButton(`id(`timed_cancel), `opt (`key_F9), Label::CancelButton() )
				     ),
			       `VSpacing(0.2)
			       )
			 )
		   );

    any which_input = nil;

    while (timeout_seconds > 0) {
	which_input = UI::TimeoutUserInput( 1000 );
	if (which_input == `timed_ok)
	    break;
	if (which_input == `timed_cancel)
	    break;
	if (which_input == `timed_stop) {
	    while (which_input == `timed_stop)
		which_input = UI::UserInput();
	    break;
	}
	timeout_seconds = timeout_seconds - 1;
	UI::ChangeWidget (`id(`remaining_time), `Value, ""+timeout_seconds);
    }
    UI::CloseDialog();

    return which_input != `timed_cancel;
}


/**
 * Generic question popup with three buttons.
 *
 * @param headline		headline or Popup::NoHeadline()
 * @param message			message string
 * @param yes_button_message	label on affirmative button (on left side)
 * @param no_button_message	label on negating button (middle)
 * @param retry_button_message	label on retry button (on right side)
 * @param focus			`focus_yes (first button), `focus_no (second button) or
 *				`focus_retry (third button)
 *
 * @return - `yes:  first button has been clicked
 *	   - `no: second button has been clicked
 *	   - `retry: third button has been clicked
 *
 * @see AnyQuestion
 *
 * @example Popup::AnyQuestion3( Label::WarningMsg(), _("... failed"), _("Continue"), _("Cancel"), _("Retry"), `focus_yes );
 */
global define symbol AnyQuestion3( string headline,
				   string message,
				   string yes_button_message,
				   string no_button_message,
				   string retry_button_message,
				   symbol focus )
{
    term yes_button = `Empty();
    term no_button = `Empty();
    term retry_button = `Empty();

    if ( focus == `focus_no )
    {
	yes_button = `PushButton( `id(`yes),
				  `opt (`key_F10),
				  yes_button_message );
	no_button  = `PushButton( `id(`no),
				  `opt(`default, `key_F9),
				  no_button_message );
	retry_button  = `PushButton( `id(`retry),
				     `opt (`key_F6),
				     retry_button_message );
    }
    else if ( focus == `focus_yes )
    {
	yes_button = `PushButton(`id(`yes),
				 `opt(`default, `key_F10),
				 yes_button_message);
	no_button  = `PushButton( `id(`no),
				  `opt (`key_F9),
				  no_button_message );
	retry_button  = `PushButton( `id(`retry),
				     `opt (`key_F6),
				     retry_button_message );
    }
    else
    {
	yes_button = `PushButton(`id(`yes),
				 `opt (`key_F10),
				 yes_button_message);
	no_button  = `PushButton( `id(`no),
				  `opt (`key_F9),
				  no_button_message );
	retry_button  = `PushButton( `id(`retry),
				     `opt(`default, `key_F6),
				     retry_button_message );
    }

    term button_box =  `HBox(
			     `HWeight( 1, yes_button),
			     `HSpacing(2),
			     `HWeight( 1, no_button ),
			     `HSpacing(2),
			     `HWeight( 1, retry_button )
			     );

    UI::OpenDialog(
		   `opt(`decorated),
		   popupLayoutInternal( headline, message, NoIcon(), button_box )
		   );

    symbol ret = (symbol)UI::UserInput();
    UI::CloseDialog();

    return ret;
}


/**
 * Special error popup for YCP modules that don't work.
 *
 * The user can choose one of:
 * - "back" - go back to the previous module
 * - "next" - skip this faulty module and directly go to the next one
 * - "again" - try it again (after fixing something in the code, of course)
 * - "cancel" - exit program
 *
 * @screenshot screenshots/ModuleError.png
 *
 * @param text	 string
 * @return symbol `back, `again, `cancel, `next
 *
 * @example Popup::ModuleError( "The module " + symbolof(argterm) + " does not work." );
 */
global define symbol ModuleError(string text)
{
    UI::OpenDialog(
		   `opt(`decorated, `warncolor),
		   `HBox(
			 `HSpacing(1),
			 `VBox(
			       `VSpacing(0.2),
			       `Heading(text),
			       `HBox(
				     `PushButton(`id(`back),   `opt (`key_F8),  Label::BackButton()  ),
				     `PushButton(`id(`again),  `opt (`key_F6),  Label::RetryButton() ),
				     `PushButton(`id(`cancel), `opt (`key_F9),  Label::QuitButton()  ),
				     `PushButton(`id(`next),   `opt (`key_F10), Label::NextButton()  )
				     ),
			       `VSpacing(0.2)
			       ),
			 `HSpacing(1)
			 )
		   );
    symbol ret = (symbol)UI::UserInput();
    UI::CloseDialog();
    return ret;
}


/**
 * Internal version of AnyTimedMessage
 *
 * Show a message with optional headline above and
 * wait until user clicked "OK" or until a timeout runs out.
 *
 * @param headline	optional headline or Popup::NoHeadline()
 * @param message	the message (maybe multi-line) to display.
 * @param icon_name	icon name (with full path) or Popup::NoIcon()
 * @param timeout	After timeout seconds dialog will be automatically closed
 *
 * @return void
 **/
define void anyTimedMessageInternal(string headline,
				    string message,
				    string icon_name,
				    integer timeout )
{
    term button_box = `PushButton( `id(`ok_msg), `opt(`default, `key_F10), Label::OKButton() );
    UI::OpenDialog(
		   `opt(`decorated),
		   popupLayoutInternalWithLabel( headline, message, icon_name,
						 button_box, sformat("%1", timeout) )
		   );

    UI::SetFocus(`id(`ok_msg));


    symbol button = nil;

    while ( timeout > 0 && button != `ok_msg)
    {
	button = (symbol) UI::TimeoutUserInput( 1000 );
	timeout = timeout - 1;

	UI::ChangeWidget(`id(`label), `Value, sformat("%1", timeout ));
    }

    UI::CloseDialog();
}


/**
 * Generic message popup
 *
 * Show a message with optional headline above and
 * wait until user clicked "OK" or until a timeout runs out.
 *
 * @param headline	optional headline or Popup::NoHeadline()
 * @param message	the message (maybe multi-line) to display.
 * @param timeout	After timeout seconds dialog will be automatically closed
 *
 * @return void
 *
 */
global define void AnyTimedMessage(string headline, string message, integer timeout)
{
    anyTimedMessageInternal( headline, message, nil, timeout );
}


/**
 * Show a warning message and wait specified amount of time or until user clicked "OK".
 *
 * @screenshot screenshots/TimedWarningPopup.png
 *
 * @param message warning message string
 * @param timeout_seconds time out in seconds
 *
 * @return void
 *
 * @see Warning
 */
global define void TimedWarning(string message, integer timeout_seconds)
{
    anyTimedMessageInternal( Label::WarningMsg(), message, popupIcon( "msg_warning.png" ), timeout_seconds );
}


/**
 * Show an error message and wait specified amount of time or until user clicked "OK".
 *
 * @screenshot screenshots/TimedErrorPopup.png
 *
 * @param message	error message string
 * @param timeout_seconds time out in seconds
 *
 * @return void
 *
 * @see Error
 */
global define void TimedError(string message, integer timeout_seconds)
{
    anyTimedMessageInternal( Label::ErrorMsg(), message, popupIcon( "msg_error.png" ), timeout_seconds );
}


// it is misaligned because there used to be UI() around it

/**
 * Show the contents of an entire file in a popup.
 *
 * @param headline	headline text
 * @param text	text to show
 * @param timeout	text to show
 *
 * @example Popup::ShowText ("Boot Messages", "kernel panic");
 */
global define void ShowTextTimed (string headline, string text,
				  integer timeout)
{
    term heading = `Empty( );

    if ( size(headline) == 0 )
    {
	heading = `VSpacing(0.2);
    }
    else
    {
	heading = `Heading( headline );
    }

    UI::OpenDialog( `opt(`decorated ),
		    `VBox(
			  `HSpacing( 70 ),	// force width
			  heading,
			  `VWeight( 1,
				    `HBox(
					  `VSpacing( 18 ),	// force height
					  `HSpacing( 0.7 ),
					  `RichText(`id(`text), `opt(`plainText), text),
					  `HSpacing( 0.7 )
					  )
				    ),
                          `VSpacing( 0.3 ),
                          `Label(`id(`label), sformat("%1", timeout)),
                          `VSpacing(0.2),
                          `PushButton(`id(`ok_msg),  `opt(`default, `key_F10), Label::OKButton() ),
			  `VSpacing( 0.3 )
			  )
		    );

    symbol button = nil;

    while ( timeout > 0 && button != `ok_msg)
    {
	button = (symbol) UI::TimeoutUserInput( 1000 );
	timeout = timeout - 1;

	UI::ChangeWidget(`id(`label), `Value, sformat("%1", timeout ));
    }

    UI::CloseDialog();
}


/**
 * Show the contents of an entire file in a popup.
 *
 * @param headline	headline text
 * @param text	text to show
 *
 * @example Popup::ShowText ("Boot Messages", "kernel panic");
 */
global define void ShowText (string headline, string text)
{
    term heading = `Empty( );

    if ( size(headline) == 0 )
    {
	heading = `VSpacing(0.2);
    }
    else
    {
	heading = `Heading( headline );
    }

    UI::OpenDialog( `opt(`decorated ),
		    `VBox(
			  `HSpacing( 70 ),	// force width
			  heading,
			  `VWeight( 1,
				    `HBox(
					  `VSpacing( 18 ),	// force height
					  `HSpacing( 0.7 ),
					  `RichText(`id(`text), `opt(`plainText), text),
					  `HSpacing( 0.7 )
					  )
				    ),
			  `VSpacing( 0.3 ),
			  `PushButton( `opt(`default, `key_F10), Label::OKButton() ), `VSpacing( 0.3 )
			  )
		    );

    UI::UserInput();
    UI::CloseDialog();
}

/**
 * Show the contents of an entire file in a popup.
 *
 * Notice: This is a WFM function, NOT an UI function!
 *
 * @param headline	headline text
 * @param filename	filename with path of the file to show
 *
 * @example Popup::ShowFile ("Boot Messages", "/var/log/boot.msg");
 */
global define void ShowFile (string headline, string filename)
{
    string text = (string)SCR::Read (.target.string, filename);

    ShowText (headline, text);
}


}
/* EOF */
