/**
 * File:	include/sysconfig/complex.ycp
 * Package:	Configuration of sysconfig
 * Summary:	Dialogs definitions
 * Authors:	Ladislav Slezak <lslezak@suse.cz>
 *
 * $Id: complex.ycp,v 1.46 2004/11/24 13:09:48 lslezak Exp $
 */

{

textdomain "sysconfig";

import "Wizard";
import "Sysconfig";
import "Mode";
import "Package";
import "String";

import "Popup";
import "Label";

include "sysconfig/helps.ycp";
include "sysconfig/routines.ycp";
include "sysconfig/dialogs.ycp";

/**
 * current selected variable in the tree widget
 */
 string selected_variable = "";

/**
 * Return a modification status
 * @return true if data was modified
 */
 define boolean Modified() ``{
    return Sysconfig::Modified();
}


/**
 * Read settings dialog
 * @return `abort if aborted and `next otherwise
 */
 define symbol ReadDialog() ``{
    Wizard::SetHelpText(Sysconfig::powertweak_mode ? HELPS["read_powertweak"]:"" : HELPS["read"]:"");

    if (Sysconfig::powertweak_mode == true)
    {
	// do not ask user to create powertweak configuration file
	// after package installation - suppose "yes"
	boolean just_installed = false;

	y2debug("Powertweak MODE");

	if (SCR::Read(.target.size, "/usr/sbin/powertweakd") < 0)
	{
	    y2warning("Package powertweak is not installed");

	    // ask user to install powertweak package
	    map ask = powertweak_install_dialog();

	    if (ask["ui"]:`cancel == `cancel)
	    {
		return `abort;
	    }

	    // install powertweak package

	    list<string> install = [ "powertweak" ];

	    if (ask["extra"]:false == true)
	    {
		install = add(install, "powertweak-extra");
	    }

	    just_installed = Package::DoInstall(install);

	    if (just_installed == false)
	    {
		Popup::Error(_("Package installation failed.
Press OK to exit.
"));
		return `abort;
	    }
	}

	y2debug("Installed packages: just_installed=%1", just_installed);

	if (SCR::Read(.target.size, Sysconfig::powertweakconfig) == -1 || SCR::Read(.target.size, Sysconfig::powertweakconfig) == 0)
	{
	    // popup dialog question
	    if (just_installed == true || Popup::AnyQuestion("", _("Powertweak configuration file does not exist.
Create it and use powerteak?
"), Label::YesButton(), Label::NoButton(), `focus_no) == true)
	    {
		// create powertweak config
		integer exit = (integer)SCR::Execute(.target.bash, "/usr/sbin/powertweakd --no-daemon --only-save");

		if (exit != 0)
		{
		    y2warning("Powertweak exit code: %1", exit);
		    // warning popup message
		    Popup::Warning(_("Powertweak failed: configuration file may be missing or broken"));
		}
	    }
	    else
	    {
		return `abort;
	    }
	}
    }

    Sysconfig::Read();

    return `next;
}

/**
 * Write settings dialog
 * @return `abort if aborted and `next otherwise
 */
 define symbol WriteDialog() ``{
    Wizard::SetHelpText(Sysconfig::powertweak_mode ? HELPS["write_powertweak"]:"" : HELPS["write"]:"");

    boolean ret = true;

    if (Sysconfig::Modified() == true)
    {
	y2milestone("Changes will be written.");
	// write and activate changes
	if (Sysconfig::Write() == false)
	{
	    // error popup message
	    Popup::Error(_("An error occurred while saving and activating the changes."));
	}
	else
	{
	    sleep(500);	// small delay, user should see 100% progress
	}
	    //Popup::Message(_("The changes were saved and successfully activated."));
    }

    return `next;
}

/**
 * Get string representation of type definition. Used at richtext description.
 * @param description Variable description
 * @param richtext result is rich/plain text
 * @return string Textual description of the type
 */
 define string possible_values(map description, boolean richtext) ``{
    string ret = "";

    string type = description["Type"]:"";

    if  (type == "")
    {
	return ret;
    }
    else if (type == "yesno")
    {
	ret = ret + "yes,no";
    }
    else if (type == "boolean")
    {
	ret = "true,false";
    }
    else if (regexpmatch(type, "^list\\(.*\\)"))
    {
	list<string> spaces = [];
	list<string> values = String::ParseOptions(regexpsub(type, "^list\\((.*)\\)", "\\1"), Sysconfig::parse_param);

	foreach(string value, values, ``{
		spaces = add(spaces, mergestring(splitstring(value, " "), "&nbsp;"));
	    }
	);

	ret = mergestring(spaces, ", ");
    }
    else if (regexpmatch(type, "^string\\(.*\\)"))
    {
	list<string> spaces = [];
	list<string> values = String::ParseOptions(regexpsub(type, "^string\\((.*)\\)", "\\1"), Sysconfig::parse_param);

	foreach(string value, values, ``{
		spaces = add(spaces, mergestring(splitstring(value, " "), "&nbsp;"));
	    }
	);

	// suffix added to the allowed (predefined) values
	ret = mergestring(spaces, ", ") + (richtext?" <I>":" ") + _("or any value") + (richtext?"</I>":"");
    }
    else if (regexpmatch(type, "^regexp\\(.*\\)"))
    {
	string regex = regexpsub(type, "^regexp\\((.*)\\)", "\\1");
	// Translation: description of possible values, regular expression string is added after the text
	ret = (richtext?"<I>":"") + _("Value Matching Regular Expression:") + (richtext?"</I>":"") + regex;
    }
    else if (type == "integer")
    {
	// allowed value description
	ret = (richtext?"<I>":"") + _("Any integer value") + (richtext?"</I>":"");
    }
    else if (regexpmatch(type, "^integer\\(.*:.*\\)"))
    {
	string min = regexpsub(type, "^integer\\((.*):.*\\)", "\\1");
	string max = regexpsub(type, "^integer\\(.*:(.*)\\)", "\\1");

	y2milestone("min: %1, max: %2", min, max);

	if (max == "" && min != "")
	{
	    // allowed value description
	    ret = (richtext?"<I>":"") + sformat(_("Integer value greater or equal to %1"), min) + (richtext?"</I>":"");
	}
	else if (min == "" && max != "")
	{
	    // allowed value description
	    ret = (richtext?"<I>":"") + sformat(_("Integer value less or equal to %1"), max) + (richtext?"</I>":"");
	}
	else
	{
	    // Translation: allowed value description, %1 is minimum value, %2 is maximum integer value
	    ret = (richtext?"<I>":"") + sformat(_("Any integer value from %1 to %2"), min, max) + (richtext?"</I>":"");
	}
    }
    else if (type == "string")
    {
	// allowed value description - any value is allowed
	ret = (richtext?"<I>":"") + _("Any value") + (richtext?"</I>":"");
    }
    else if (type == "ip")
    {
	// allowed value description - IP adress
	ret = (richtext?"<I>":"") + _("IPv4 or IPv6 address") + (richtext?"</I>":"");
    }
    else if (type == "ip4")
    {
	// allowed value description - IPv4 adress
	ret = (richtext?"<I>":"") + _("IPv4 address") + (richtext?"</I>":"");
    }
    else if (type == "ip6")
    {
	// allowed value description - IPv6 adress
	ret = (richtext?"<I>":"") + _("IPv6 address") + (richtext?"</I>":"");
    }
    else
    {
	y2warning("Unknown type definition: %1", type);
    }

    return ret;
}

/**
 * Create rich text description string from description values
 * @param description Description
 * @param richtext if true result is rich text, if false result is plain text
 * @return string Rich text string
 */
 define string create_description(map<string, any> description, boolean richtext) ``{
    string varname = description["name"]:"";
    string file = description["file"]:"";
    string default_value = (string) (description["Default"]:nil);
    string comment = description["comment"]:"";

    string possible_vals = possible_values(description, richtext);

    string result = "";

    if (file != "" && file != nil)
    {
	// rich text item
	result = result + ((richtext) ? "<P><B>":"") + _("File: ") + ((richtext) ?"</B> ":"") + file + ((richtext) ? "</P>":"\n");
    }

    if (possible_vals != "" && possible_vals != nil)
    {
	// rich text item
	result = result + ((richtext) ? "<P><B>":"") + _("Possible Values: ") + ((richtext) ?"</B> ":"") + possible_vals + ((richtext) ?"</P>":"\n");
    }

    if (default_value != nil && size(file) > 0)
    {
	// TODO: replace empty value by special text (e.g. "</I>empty</I>")

	// rich text value
	result = result + ((richtext) ?"<P><B>":"") + _("Default Value: ") + ((richtext) ?"</B> ":"") + default_value + ((richtext) ?"</P>":"\n");
    }

    // if value was modified add original value
    if (haskey(description, "new_value"))
    {
	string original = description["value"]:"";

	// quote empty value
	if (original == "")
	{
	    original = "\"\"";
	}
	// rich text value
	result = result + ((richtext) ?"<P><B>":"") + _("Original Value: ") + ((richtext) ?"</B> ":"") + original + ((richtext) ?"</P>":"\n");
    }

    if (haskey(description, "actions"))
    {
	// display specified action command
	string conf_modules = (string) (description["actions", "Cfg"]:nil);
	string restart = (string) (description["actions", "Rest"]:nil);
	string reload = (string) (description["actions", "Reld"]:nil);
	string command = (string) (description["actions", "Cmd"]:nil);
	string precommand = (string) (description["actions", "Pre"]:nil);

	// check whether action is defined
	if (precommand != nil && size(precommand) > 0)
	{
	    // header in the variable description text, bash command is appended
	    result = result + ((richtext) ?"<P><B>":"") + _("Prepare Command: ") + ((richtext) ?"</B> ":"") + precommand + ((richtext) ?"</P>":"\n");
	}

	if (conf_modules != nil && size(conf_modules) > 0)
	{
	    // parse string with options, then add them to the rich text
	    list<string> conf = String::ParseOptions(conf_modules, Sysconfig::parse_param);
	    // header in the variable description text, SuSEconfig/ULconfig module names are appended
	    result = result + ((richtext) ?"<P><B>":"") + _("Configuration Script: ") + ((richtext) ?"</B> ":"") + mergestring(conf, ", ") + ((richtext) ?"</P>":"\n");
	}

	if (reload != nil && size(reload) > 0)
	{
	    list<string> services = String::ParseOptions(reload, Sysconfig::parse_param);
	    // header in the variable description text, service names (e.g. "apache") are appended
	    result = result + ((richtext) ?"<P><B>":"") + _("Service to Reload: ") + ((richtext) ?"</B> ":"") + mergestring(services, ", ") + ((richtext) ?"</P>":"\n");
	}

	if (restart != nil && size(restart) > 0)
	{
	    list<string> services = String::ParseOptions(restart, Sysconfig::parse_param);
	    // header in the variable description text, service names (e.g. "apache") are appended
	    result = result + ((richtext) ?"<P><B>":"") + _("Service to Restart: ") + ((richtext) ?"</B> ":"") + mergestring(services, ", ") + ((richtext) ?"</P>":"\n");
	}

	if (command != nil && size(command) > 0)
	{
	    // header in the variable description text, bash command is appended
	    result = result + ((richtext) ?"<P><B>":"") + _("Activation Command: ") + ((richtext) ?"</B> ":"") + command + ((richtext) ?"</P>":"\n");
	}
    }

    if (comment != "" && comment != nil)
    {
	if (richtext)
	{
	    // convert '<' and '>' to '&lt;' '&gt;'
	    comment = mergestring(splitstring(comment, "<"), "&lt;");
	    comment = mergestring(splitstring(comment, ">"), "&gt;");

	    // keep comment formatting:
	    // convert '\n' => '<BR>'
	    comment = mergestring(splitstring(comment, "\n"), "<BR>");

	    // do not change node descriptions
	    if (file != "")
	    {
		// convert ' ' => '&nbsp;'
		comment = mergestring(splitstring(comment, " "), "&nbsp;");
	    }

	    y2debug("formatted comment: %1", comment);
	}

	// rich text value
	result = result + ((richtext) ?"<P><B>":"") + _("Description: ") + ((richtext) ?"</B><BR> ":"") + comment + ((richtext) ?"</P>":"");
    }

    y2debug("variable description : %1", result);

    return result;
}

/**
 * Get combo box editable status - depends on Type value
 * @param description Description of variable
 * @return boolean True if combo box should be editable
 */
 define boolean combo_editable(map description) ``{
    string type = description["Type"]:"";

    return (type == "" || regexpmatch(type, "^integer\\(.*:.*\\)$") || type == "integer"
	|| type == "string" || regexpmatch(type, "^string\\(.*\\)$") || type == "ip"
	|| regexpmatch(type, "^regexp\\(.*\\)$"));
}

/**
 * Generic list function - add value to the list if it isn't already there
 * @param l Input list
 * @param v Input value
 * @return list List with value v
 */
 define list add_if_missing(list l, string v) ``{
    return (!contains(l, v)) ? add(l, v) : l;
}

/**
 * Escape double quotes and back slash characters by back slash
 * @param input String to escape
 * @return Escaped string
 */
 define string backslash_add(string input) ``{

    // escape double quotes and back slashes
    string escaped = "";
    integer pos = 0;

    while (pos < size(input))
    {
	string ch = substring(input, pos, 1);
	string ch_1 = substring(input, pos + 1, 1);

	// don't add backslash before \$ (#34809)
	if (ch == "\\" && ch_1 != nil && ch_1 != "$")
	{
	    escaped = escaped + "\\\\";
	}
	else
	{
	    if (ch == "\"")
	    {
		escaped = escaped + "\\\"";
	    }
	    else if (ch == "\n")
	    {
		// multi line value
		escaped = escaped + "\\\n";
	    }
	    else
	    {
		escaped = escaped + ch;
	    }
	}

	pos = pos + 1;
    }

    return escaped;
}

/**
 * Remove backslashes from string - opposite funtion to the backslash_add function.
 * @param input Escaped string
 * @return string String without escape chars
 */
 define string backslash_remove(string input) ``{

    if (input == nil)
    {
	return nil;
    }

    string  ret = "";
    integer index = 0;
    boolean backslash = false;

    while (index < size(input))
    {
	string ch = substring(input, index, 1);
	string ch_1 = substring(input, index + 1, 1);

	if (backslash == false)
	{
	    // don't remove backslash before \$ (#34809)
	    if (ch == "\\" && ch_1 != nil && ch_1 != "$")
	    {
		backslash = true;
	    }
	    else
	    {
		ret = ret + ch;
	    }
	}
	else
	{
	    ret = ret + ch;
	    backslash = false;
	}

	index = index + 1;
    }

    return ret;
}

/**
 * Create list of values for combo box widget
 * @param description Variable description
 * @param set_default If true add default value to the list
 * @return list List of values for combo box widget
 */
 define list combo_list(map<string, any> description, boolean set_default) ``{
    string new_value = (string) (description["new_value"]:nil);
    string value = description["value"]:"";

    // use default value (or emty string) instead of the curent value in autoyast
    if (Mode::config())
    {
	value = (haskey(description, "Default")) ? description["Default"]:"" : "";
    }

    if (description["actions","SingleQt"]:"" == "")
    {
	new_value = backslash_remove(new_value);
	value = backslash_remove(value);
    }

    list ret = [];
    string deflt = (string) (description["Default"]:nil);

    if (set_default == true && deflt != nil)
    {
	y2debug("Adding default value: %1", deflt);
	if (!contains(ret, deflt)) ret = add(ret, deflt);
    }

    if (new_value != nil)
    {
	if (!contains(ret, new_value)) ret = add(ret, new_value);
    }
    else if (value != nil)
    {
	if (!contains(ret, value)) ret = add(ret, value);
    }


    string type = description["Type"]:"";

    if (type == "yesno")
    {
	if (!contains(ret, "yes")) ret = add(ret, "yes");
	if (!contains(ret, "no")) ret = add(ret, "no");
    }
    else if (type == "boolean")
    {
	if (!contains(ret, "true")) ret = add(ret, "true");
	if (!contains(ret, "false")) ret = add(ret, "false");
    }
    else if (regexpmatch(type, "^list\\(.*\\)"))
    {
	string values_string = regexpsub(type, "^list\\((.*)\\)", "\\1");
	list<string> parsed = String::ParseOptions(values_string, Sysconfig::parse_param);

	// add missing items
	foreach(string option, parsed,
	    ``{
		if (!contains(ret, option)) ret = add(ret, option);
	    }
	);
    }
    else if (regexpmatch(type, "^string\\(.*\\)"))
    {
	string values_string = regexpsub(type, "^string\\((.*)\\)", "\\1");
	list<string> parsed = String::ParseOptions(values_string, Sysconfig::parse_param);

	// add missing items
	foreach(string option, parsed,
	    ``{
		if (!contains(ret, option)) ret = add(ret, option);
	    }
	);
    }

    // add default value to the list
    if (deflt != nil && !contains(ret, deflt)) ret = add(ret, deflt);

    // add old value to the list if variable was modified
    if (new_value != nil)
    {
	if (!contains(ret, value)) ret = add(ret, value);
    }

    y2debug("combo list: %1", ret);

    return ret;
}

/**
 * Update combo box in dialog
 * @param description Variable description
 * @param set_default Set to true ifdefault value should be in the combo box
 */
 define void update_combo(map<string, any> description, boolean set_default) ``{
    string varname = description["name"]:"";

    // modification flag added to variable name (if it was changed)
    string modif_flag = (haskey(description, "new_value")) ? "  " + _("(changed)") : "";

    if (combo_editable(description))
    {
	// combo box widget label - variable name is appended to the string
	UI::ReplaceWidget(`id(`replace), `ComboBox (`id(`combo), `opt(`editable, `hstretch), _("S&etting of: ") + varname + modif_flag, combo_list(description, set_default)));
    }
    else
    {
	// combo box widget label - variable name is appended to the string
	UI::ReplaceWidget(`id(`replace), `ComboBox (`id(`combo), `opt(`hstretch), _("S&etting of: ") + varname + modif_flag, combo_list(description, set_default)));
    }

    // disable combo for non-leaf nodes
    UI::ChangeWidget(`id(`combo), `Enabled, ((description["file"]:"") != ""));

    // display warning if value is not single line
    // combobox is one line entry, multiline values are merged to one line
    // (new lines are displayed as spaces, but they are correctly preserved
    string val = (description["new_value"]:nil != nil) ? description["new_value"]:"" : description["value"]:"";

    if (val != nil)
    {
	list<string> lines = splitstring(val, "\n");

	if (size(lines) > 1)
	{
	    // current value has more than one line - it is displayed incorrectly
	    // because combobox widget has single line entry (lines are merged)
	    Popup::Warning(_("The currently selected value has more than one line.
Joined lines are displayed in the combo box.
"));
	}
    }
}

/**
 * Update "Default" button state (enable/disable) in the dialog
 * @param description Variable description
 */
 define void update_button_state(map<string, any> description) ``{
    string def = (string) (description["Default"]:nil);

    UI::ChangeWidget(`id(`def), `Enabled, (def != nil));
}

/**
 * Update location text in the dialog
 * @param description Variable description
 */
 define void update_location(map description) ``{
    string l = description["location"]:"";

    // header label
    UI::ChangeWidget(`id(`heading), `Value, _("Current Selection: ") + l);
}

/**
 * Is selected item in the tree widget leaf node?
 * @param id Value from tree widget
 * @return boolean True if node is not leaf-node
 */
 define boolean is_node(string id) ``{
    return (findfirstof(id, "$") == nil);
}

/**
 * Set new value for variable, warn user if new value does not match type definition.
 * @param force_change force value as changed even if it is equal to the old one
 */
 define void check_set_current_value(boolean force_change)
``{
    // check current value
    if (selected_variable != "")
    {
	string new_value = (string)UI::QueryWidget(`id(`combo), `Value);
	map d = Sysconfig::get_description(selected_variable);

	// check whether single quotes are used in the configuration file
	if (d["actions", "SingleQt"]:"" == "")
	{
	    new_value = backslash_add(new_value);
	}

	if (Sysconfig::get_name_from_id(selected_variable) != "")
	{
	    // variable was selected (not category)
	    symbol result = Sysconfig::set_value(selected_variable, new_value, false, force_change);

	    if (result == `not_valid)
	    {
		string t = d["Type"]:"string";

		// popup question dialog: variable value does not match defined type - ask user to set value (%1 is value entered by user, %2 is allowed type - e.g. integer
		if (Popup::AnyQuestion(Label::WarningMsg(), sformat(_("Value '%1'
does not match type '%2'.

Really set this value?
"), new_value, t), Label::YesButton(), Label::NoButton(), `focus_no) == true)
		{
		    // force setting of value
		    Sysconfig::set_value(selected_variable, new_value, true, force_change);
		}
	    }
	}
    }
}

/**
 * Create table content list for selected variables
 * @param varids Variables which will be contained in the table
 * @return list Table content
 */
 define list create_table_content(list<string> varids)
``{
    list table_content = [];

    foreach(string varid, varids,
	``{
	    map descr = Sysconfig::get_description(varid);
	    string name = descr["name"]:"";
	    string old = descr["value"]:"";
	    string new = descr["new_value"]:"";
	    string file = descr["file"]:"";

	    // display only beginning of comment (to limit table space used)
	    string comm = descr["comment"]:"";
	    // remove newlines
	    comm = Sysconfig::remove_whitespaces(mergestring(splitstring(comm, "\n"), " "));

	    if (size(comm) > 90)
	    {
		comm = substring(comm, 0, 90);
		// when a comment is too long to display it in the table
		// it is shortened and mark (three dot characters) is added to the end
		comm = comm + _("...");
	    }

	    table_content = add(table_content, `item(`id(varid), name, new, old, file, comm));
	}
    );

    return table_content;
}

    list<map> GenerateTree(list<map> Tree, string parent, list<list> input) {

        foreach(list i, input, ``{
                string id = i[0,0]:"";
                string title = i[1]:"";
                boolean enabled = i[2]:false;
                list<list> children = i[3]:[];
                Tree = Wizard::AddTreeItem(Tree , parent,  title, id );
                if (size(children) > 0 ) {
                    Tree = GenerateTree(Tree, id, children);
                }
        });
        return Tree;
    }



/**
 * Display main configuration dialog
 * @return dialog result
 */
 define symbol MainDialog() ``{

    term button_box = `HBox(
	 // back pushbutton: the user input is ignored and the last dialog is called
	 `PushButton(`id(`abort), `opt(`key_F9), Label::AbortButton()),
	 `HStretch(),
	 `PushButton(`id(`help), `opt(`key_F1), Label::HelpButton()),
	 `HStretch(),
	 // Translation: push button label
	 `PushButton(`id(`search), _("&Search")),
	 `HStretch(),
	 `PushButton(`id(`next), `opt(`key_F10), Label::FinishButton())
    );

    // tree widget label
    // term help_space_content = `Tree(`id(`tree), `opt(`notify, `vstretch), _("&Configuration Options"), Sysconfig::tree_content);

    // Wizard::OpenCustomDialog(help_space_content, button_box);
    Wizard::CreateTreeDialog();
    list<map> Tree = GenerateTree([], "", Sysconfig::tree_content) ;
    Wizard::CreateTree(Tree, _("&Configuration Options"));

    string helptext = (Sysconfig::powertweak_mode == true) ?

		// help text (powertweak mode)
		_("<p>After you save your changes, this editor changes the variables in the
/etc/powertweak/tweaks file. Then it restarts the powertweak daemon, which activates changes.</p>
")

	    :
	    (
		// helptext for popup - part 1/3
		_("<p>After you save your changes, this editor changes the variables in the
corresponding sysconfig file. Then it starts activation commands, which changes the underlying configuration files, stops and starts daemons,
and runs low-level configuration tools so your configuration in sysconfig takes effect.</p>
") +

                // helptext for popup - part 2/3
		_("<p><b>Important:</b> You still can edit each individual configuration file manually. The name of file is displayed in the variable description.</p>") +

                // helptext for popup - part 3/3
		_("<p> SuSEconfig saves a checksum of each configuration file, so it can detect if
 you have manually changed any of them. If you have changed a configuration file manually,
 it will not touch it.</p>
")
	    );

    Wizard::SetContents(_("/etc/sysconfig Editor"),
			    `VBox(
				// label widget
				`Left(`Label(`id(`heading), `opt(`hstretch), _("Current Selection: "))),
				`VSpacing(0.5),
				`HBox(
				    `HWeight(1,
					`ReplacePoint(`id(`replace),
					    // combo box label
					    `ComboBox (`id(`combo), `opt(`disabled, `hstretch), _("S&etting of: "),  [""])
					)
				    ),
				    `VBox(
					// dummy widget used to align button
					`Label(""),
					// push button label
					`PushButton(`id(`def), `opt(`disabled), _("&Default"))
				    )
				),
				`VSpacing(1),
				// help rich text displayed after module start (1/2)
				`RichText(`id(`rt), (Sysconfig::powertweak_mode == true) ? _("<P><B>Powertweak Configuration</B></P><P>In this configuration module, you can tune some kernel and hardware options.</P><P><B>Warning:</B> This configuration module is for experts only!</P>") :
				    _("<P><B>System Configuration Editor</B></P><P>With the system configuration editor, you can change some system settings. You can also use YaST2 to configure your hardware and system settings.</P>") +
				// help rich text displayed after module start (2/2)
				    _("<P><B>Note:</B> Descriptions are not translated because thay are read directly from configuration files.</P>")),
				// push button label - displayed only in autoinstallation config mode
				(Mode::config() == true) ? `HBox(`PushButton(`id(`use_current), _("&Use Current Value")),
					// push button label - displayed only in autoinstallation config mode
					`PushButton(`id(`add_new), `opt(`key_F3), _("&Add New Variable..."))
				    ) : `Empty()
	),
	helptext, true, true);

    if (UI::WidgetExists(`id(`back)))
    {
        UI::ChangeWidget(`id(`back), `Label,  _("&Search") );
    }
    UI::ChangeWidget(`id(`next), `Label, Label::FinishButton() );
    if (UI::WidgetExists(`id(`wizardTree)))
    {
        UI::ReplaceWidget(`id(`rep_button_box ), button_box );
    }
    Wizard::SetDesktopIcon("sysconfig");

    any ret = nil;

    while(ret != `cancel && ret != `abort && ret != `next )
    {
        map event = UI::WaitForEvent();
        ret = event["ID"]:nil;


	// "Default" button
	if (ret == `def)
	{
	    map<string, any> description = Sysconfig::get_description(selected_variable);
	    update_combo(description, true);
	}
	else if (ret == `next)
	{
	    // check if current value was modified
	    check_set_current_value(false);

	    list<string> modified = Sysconfig::get_modified();

	    // show table with modified variables
	    if (size(modified) > 0)
	    {

		y2milestone("Modified variables: %1", modified);

		// popup dialog header
		map result = display_variables_dialog(_("Modified Variables"),
// help text in popup dialog
_("Here, see the values YaST2 will change.
Choose \"OK\" for YaST2 to save these changes.
Choose \"Cancel\" to edit the values again.
"),			create_table_content(modified), Label::OKButton(), Label::CancelButton(), ((Sysconfig::powertweak_mode == false) ? _("Confirm Each Activation Command") : ""), false);

		if (result["ui"]:`dummy == `cancel)
		{
		    ret = `again;
		}

		// set confirmation flag
		Sysconfig::ConfirmActions = result["checkbox"]:false;
	    }
	}
	else if (ret == `back || ret == `search) // This is for Search actually FIXME
	{
	    map search_parameters = display_search_dialog();

	    if (search_parameters != $[])
	    {
		list<string> found = Sysconfig::search(search_parameters, true);

		if (size(found) > 0)
		{
		    // // popup dialog header
		    map<string, any> input = display_variables_dialog(_("Search Result"),
			// help text in popup dialog
			_("The search results are displayed here. If you see the item you want, select it then click \"Go to\". Otherwise, click \"Cancel\" to close this dialog."), create_table_content(found),
			// push button label
			 _("&Go to"), Label::CancelButton(), "", nil);

		    if (input["ui"]:`dummy == `cancel)
		    {
			ret = `again;
		    }
		    else
		    {
			string sel = (string) (input["selected"]:nil);
			if (sel != nil)
			{
			    // select variable in the tree
			    //UI::ChangeWidget(`id(`tree), `CurrentItem, sel);
                            Wizard::SelectTreeItem(sel);

			    // display selected variable
                            if (UI::WidgetExists(`id(`wizardTree)))
			        ret = `wizardTree;
                            else
                                ret = sel;
			}
		    }
		}
		else
		{
		    // popup message - search result message
		    Popup::Message(_("No entries found"));
		}
	    }
	}
        else if (ret == `help)
        {
            UI::OpenDialog( `opt(`decorated ),
                `HBox( `VSpacing(16),
                       `VBox(
                           `HSpacing(60),
                           // popup window header
                           `Heading(_("Help")),
                           `VSpacing(0.5),
                           `RichText(helptext),
                           `VSpacing(1.5),
                            // push button label
                           `PushButton(`id(`ok), `opt(`default, `key_F10), Label::OKButton())
                           )
                    )
            );

            UI::SetFocus(`id(`ok));
            UI::UserInput();
            UI::CloseDialog();
        }

	else if (ret == `abort || ret == `cancel)
	{
	    if (!ReallyAbort())
	    {
		ret = nil;
	    }
	    else
	    {
		// `cancel is same as `abort
		ret = `abort;
	    }
	}
	// autoinstallation config mode only
	else if (ret == `use_current)
	{
	    // force current value as changed
	    check_set_current_value(true);

	    map<string, any> description = Sysconfig::get_description(selected_variable);

	    // update combo box - add "changed" status
	    update_combo(description, false);
	}
	// autoinstallation config mode only
	else if (ret == `add_new)
	{
	    // ask user for new variable name, value and location (file name)
	    map in = add_new_variable();

	    symbol ui = in["ui"]:`cancel;
	    string name = in["name"]:"";
	    string file = in["file"]:"";
	    string value = in["value"]:"";

	    if (ui == `ok)
	    {
		Sysconfig::set_value(sformat("%1$%2", name, file), value, false, true);
	    }
	}

	if (ret == `wizardTree || is(ret, string))
	{
	    check_set_current_value(false);

	    // string selected = (string)UI::QueryWidget(`id(`tree), `CurrentItem);
            string selected = Wizard::QueryTreeItem();
	    selected_variable = selected;
	    y2milestone("Selected: %1", selected);

	    map<string, any> description = Sysconfig::get_description(selected);

	    y2milestone("Descr: %1", description);

	    // update richtext content
	    UI::ChangeWidget(`id(`rt), `Value, create_description(description, true));

	    // update combo box
	    update_combo(description, false);

	    // update "Default" button state (enable/disable)
	    update_button_state(description);

	    // update location in header
	    update_location(description);
	}
    }

    UI::CloseDialog();

    return (symbol)ret;
}

}
