/**
 * File:
 *      include/bootloader/routines/widgets.ycp
 *
 * Module:
 *      Bootloader installation and configuration
 *
 * Summary:
 *      Common widgets for being used by several bootloaders
 *
 * Authors:
 *      Jiri Srain <jsrain@suse.cz>
 *
 * $Id: widgets.ycp 20246 2004-11-09 07:17:07Z jsrain $
 *
 */


{

    textdomain "bootloader";

    import "CWM";
    import "Label";
    import "Mode";
    import "Storage";
    import "StorageDevices";
    import "TablePopup";

    include "bootloader/routines/options.ycp";
    include "bootloader/routines/popups.ycp";


// generic T/P functions

    /**
      * Fallback init function of a popup
      * @param opt_id any option id
      * @param opt_key any option key
      */
    global define void commonPopupInit (any opt_id, string opt_key) ``{
	integer index = (integer)opt_id;
	if (index != nil)
	{
	    any value = BootCommon::current_section[index, "value"]:nil;
	    if (value == nil)
		value = "";
	    UI::ChangeWidget (`id (opt_key), `Value, value);
	}
	UI::SetFocus (`id (opt_key));
    }

    /**
      * Fallback store function of a popup
      * @param opt_id any option id
      * @param opt_key any option key
      */
    global define void commonPopupStore (any opt_id, string opt_key) ``{
	any value = UI::QueryWidget (`id (opt_key), `Value);
	one_section_changed = true;
	if (opt_id == nil) // append
	{
	    current_section = appendValue (
		current_section, (string)opt_key, value);
	}
	else if (is (opt_id, integer))
	{
	    integer index = (integer)opt_id;
	    current_section[index, "value"] = value;
	    current_section[index, "changed"] = true;
	}
    }

    /**
      * Fallback init function of a popup
      * @param opt_id any option id
      * @param opt_key any option key
      * @return boolean true if item was changed
      */
    global define boolean commonTableChanged (any opt_id, string opt_key) ``{
	if (is (opt_id, integer))
	{
	    integer index = (integer)opt_id;
	    return current_section[index, "changed"]:false;
	}
	else if (is (opt_id, string))
	{
	    string key = (string)opt_id;
	    map intern = (map)lookup_value (current_section, "__intern__", $[]);
	    list ch = intern["changed_opts"]:[];
	    return contains (ch, opt_id);
	}
	return false;
    }

    /**
      * Fallback summary function of a table entry
      * @param opt_id any option id
      * @param opt_key any option key
      * @return string summary to the table
      */
    global define string commonTableEntrySummary (any opt_id, string opt_key)``{
	if (is (opt_id, integer))
	{
	    integer opt_id_int = (integer)opt_id;
	    any value = BootCommon::current_section[opt_id_int, "value"]:nil;
	    if (value == nil)
		value = "";
	    if (is (value, boolean))
	    {
		return (boolean)value
		    // table item
		    ? _("Yes")
		    // table item
		    : _("No");
	    }
	    return sformat ("%1", value);
	}
	return "";
    }

    /**
      * Handler for the delete event on the table
      * @param opt_id any option id
      * @param opt_key any option key
      * @return boolean true if item was really deleted
      */
    global define boolean commonTableEntryDelete (any opt_id, string opt_key) ``{
	if (! confirmOptionDeletePopup ())
	    return false;
	if (! is (opt_id, integer))
	    return false;

	integer index = (integer)opt_id;
	current_section = remove (current_section, index);
	one_section_changed = true;
	return true;
    }

    /**
      * Create a list of options for the table
      * @param descr map description of the table
      * @return list of option ids as it should be displayed
      */
    global define list getTableContents (map descr) ``{
	if (BootCommon::getLoaderType (false) == "none")
	    return ["__loader_type"];
	list prependlist = [];
	list forbidden_keys = [];
	any type = BootCommon::getSectionType (BootCommon::current_section);
	if (type == `global)
	{
	    prependlist = ["__loader_type", "__sep2", "__location",
		"default", "__sections", "__activate", "__repl_mbr",
		"__backup_mbr", "__save_method", "__sep1"];
	    forbidden_keys = ["boot", "default", "activate"];
	}
	else
	{
	    prependlist = [getSectionTitle (), "__section_type", "__sep1"];
	}
	return createSectionItemsList (prependlist, forbidden_keys, true);
    }



    /**
      * Transform table entry id to option id
      * @param table map table description
      * @param opt_id any entry id
      * @return any option key
      */
    global define string id2key (map table, any opt_id) ``{
	if (is (opt_id, string))
	{
	    if (substring ((string)opt_id, 0, 5) == "__sep")
		return "____sep";
	    return (string)opt_id;
	}
	integer index = (integer)opt_id;
	return BootCommon::current_section[index, "key"]:"";
    }

    /**
      * Get the popup widget description map
      * @param opt_key any option key
      * @return map popup description map
      */
    global define map key2descr (string opt_key) ``{
        map ret = (map)(cwm_options[opt_key]:nil);
        if (ret != nil)
            return ret;
        return $[
	    "init" : BootCommon::commonPopupInit,
	    "store" : BootCommon::commonPopupStore,
        ];
    }


// sections managment widget


    /**
      * Refresh and redraw widget wits sections
      * @param sects list of current sections
      */
    global define void sectionsRedrawTable (list<list<map> > sects) ``{
	boolean elilo = getLoaderType (false) == "elilo";
	list sec = maplist (list<map> s, sects, ``{
	    string image = (string)lookup_value (s,  getKernelKey (), nil);
	    string root = (string)lookup_value (s, "root", "");
            return `item (`id (lookup_value (s, getSectionTitle (), "")),
                tolower ((string)lookup_value (current_globals, "default", ""))
			== tolower ((string)lookup_value (
			    s, getSectionTitle (), ""))
                    ? UI::Glyph (`CheckMark) : "",
                (string)lookup_value (s, getSectionTitle (), ""),
                image == nil ? _("other") : _("image"),
                (image != "" && image != nil)
                ? sformat ("%1   (%2%3)",
                    image,
                    elilo
			? ""
			: BootCommon::splitPath (image)[0]:"",
                    root == "" ? ""
                        : ((elilo ? "" : ", ")
			    + sformat ("root=%1", root)))
                : (string)lookup_value (s, "other", "")
            );
        });
	UI::ChangeWidget (`id (`sects), `Items, sec);
    }

    /**
      * Init function of widget
      * @param widget any id of the widget
      */
    global define void sectionsInit (string widget) ``{
	sectionsRedrawTable (BootCommon::current_sections);
	UI::SetFocus (`id (`sects));
    }

    /**
      * Handle function of a widget
      * @param widget any widget key
      * @param event map event description of event that occured
      * @return symbol to return to wizard sequencer, or nil
      */
    global define symbol sectionsHandle (string widget, map event) ``{
	any op = event["ID"]:nil;
	if (event["ID"]:nil == `sects
	    && event["EventReason"]:"" == "Activated"
	    && event["EventType"]:"" == "WidgetEvent")
	{
	    op = `edit;
	}
	y2milestone ("Handling event %1", op);
	string current = (string)UI::QueryWidget (`id (`sects), `CurrentItem);
	integer counter = 0;
	integer index = 0;
	foreach (list<map> s, BootCommon::current_sections, ``{
	    if ((string)lookup_value (s, getSectionTitle (), "") == current)
		index = counter;
	    counter = counter + 1;
	});
	list<list<map> > sects = BootCommon::current_sections;
	if (op == `up)
	{
	    if (index > 0)
	    {
		sects = (list<list<map> >)swapItems(sects, index, index - 1);
		index = index - 1;
		BootCommon::current_sections = sects;
		sectionsRedrawTable (sects);
		UI::ChangeWidget (`id (`sects), `CurrentItem,
		    lookup_value (sects[index]:[], getSectionTitle (), 0));
		BootCommon::sections_changed = true;
	    }
	}
	else if (op == `down)
	{
	    if (index < (size(sects) - 1))
	    {
		sects = (list<list<map> >)swapItems(sects, index, index + 1);
                index = index + 1;
		BootCommon::current_sections = sects;
		sectionsRedrawTable (sects);
		UI::ChangeWidget (`id (`sects), `CurrentItem,
		    lookup_value (sects[index]:[], getSectionTitle (), 0));
		BootCommon::sections_changed = true;
	    }
	}
	else if (op == `default)
	{
	    addValueToBootloaderSection (`current_globals, "default", current);
	    sectionsRedrawTable (sects);
	    UI::ChangeWidget (`id (`sects), `CurrentItem, current);
	    BootCommon::sections_changed = true;
	}
	else if (op == `add)
	{
	    list<map> selected = sects[index]:[];
	    string name = (string)lookup_value (selected,
		BootCommon::getSectionTitle (), "");
	    if (size (current_sections) > 0 && askClone (name))
	    {
		selected = modifySection (
		    (list<map>)eval (selected),
		    BootCommon::getSectionTitle (),
		    BootCommon::getFreeSectionLabel ());
		BootCommon::current_section = selected;
	    }
	    else
	    {
		BootCommon::current_section = BootCommon::convertSection
		    (nil, "image", nil);
	    }
	    BootCommon::current_section_name = "";
	    BootCommon::one_section_changed = true;
	    return `add;
	}
	else if (op == `edit)
	{
	    y2milestone ("Editing title %1, index %2", current, index);
	    BootCommon::current_section = (list<map>)eval (sects[index]:[]);
	    BootCommon::current_section_name
		= (string)lookup_value (sects[index]:[], getSectionTitle (),"");
	    BootCommon::one_section_changed = false;
	    return `edit;
	}
	else if (op == `delete && confirmSectionDeletePopup (current))
	{
	    map del_intern = (map)
		lookup_value (sects[index]:[], "__intern__", $[]);
	    if (del_intern["type"]:"" == "memtest86")
	    {
		BootCommon::memtest_removed = true;
	    }
	    sects = remove (sects, index);
	    if (current ==lookup_value (current_globals,
		"default", ""))
	    {
		addValueToBootloaderSection (`current_globals, "default",
		    lookup_value (sects[0]:[], getSectionTitle (), ""));
	    }
	    BootCommon::current_sections = sects;
	    sectionsRedrawTable (sects);
	    BootCommon::sections_changed = true;
	}
	UI::SetFocus (`id (`sects));
	return nil;
    }

    /**
      * Store function of a widget
      * @param widget any widget key
      * @param event map event description of event that occured
      */
    global define void sectionsStore (string widget, map event) ``{
	any op = event["ID"]:nil;
	if (op != `edit && op != `add)
	{
	    if (BootCommon::sections_changed)
	    {
		BootCommon::changed = true;
		BootCommon::current_section = BootCommon::current_globals;
		BootCommon::markGeneralOptionChanged ("__sections");
		BootCommon::current_globals = BootCommon::current_section;
		BootCommon::current_globals = maplist (map m,
		    BootCommon::current_globals,
		``{
		    if (m["key"]:"" == getDefaultKey ())
			m["changed"] = true;
		    return m;
		});
	    }
	}

    }

    /**
      * Get map of widget
      * @return map of widget
      */
    global define map<string,any> getSectionsWidget () ``{
	return $[
	    "widget" : `custom,
	    "custom_widget" : `VBox (
		`HBox (
			`Table (`id (`sects), `opt (`keepSorting, `immediate, `notify), `header (
			// table header
			_("Def."),
			// table header
			_("Label"),
			// table header
			_("Type"),
			// table header
			_("Image / Device")
			), []),
		    `HSpacing (1),
		    `VBox (
			`VStretch (),
			// pushbutton
			`PushButton (`id (`up), _("&Up")),
			// pushbutton
			`PushButton (`id (`down), _("&Down")),
			`VStretch ()
		    )
		),
		`HBox (
		    `PushButton (`id (`add), `opt (`key_F3), Label::AddButton ()),
		    `PushButton (`id (`edit), `opt (`key_F4), Label::EditButton ()),
		    `PushButton (`id (`delete), `opt (`key_F5), Label::DeleteButton ()),
		    `HStretch (),
			// pushbutton
		    `PushButton (`id (`default), _("Set as De&fault"))
		)
	    ),
	    "init" : BootCommon::sectionsInit,
	    "handle" : BootCommon::sectionsHandle,
	    "store" : BootCommon::sectionsStore,
	    "help" : getSectionsHelp (),
	];
    }

// single section widget


    /**
      * Handle function of a widget
      * @param widget string widget key
      * @param event map event description of event that occured
      * @return symbol to return to wizard sequencer, or nil
      */
    global define symbol singleSectionHandle (string widget, map event) ``{
	redraw_table = false;
	map<string,any> current_table = CWM::GetProcessedWidget ();
	map options = current_bootloader_attribs["option_list"]:$[];
	string sect_type = (string)getSectionType (current_section);
	current_table["add_items"] = options[sect_type]:[];
	symbol ret = TablePopup::TableHandle (current_table, widget, event);
	if (redraw_table)
	{
	    TablePopup::TableInit (current_table, widget);
	}
	return ret;
    }

    /**
      * Store function of a widget
      * @param widget any widget key
      * @param event map event description of event that occured
      */
    global define void singleSectionStore (string widget, map event) ``{
	string ls = getSectionTitle ();
	string old_name = (string)current_section_name;
	y2milestone ("Old name: %1", old_name);
	map intern = (map)lookup_value (current_section, "__intern__", $[]);
	intern["changed"] = true;
	current_section = modifySection (current_section, "__intern__", intern);
	boolean found = false;
	BootCommon::current_sections = maplist (list<map> s, current_sections, ``{
	    if (lookup_value (s, ls, "") == old_name)
	    {
		found = true;
		return BootCommon::current_section;
	    }
	    else
	    {
		return s;
	    }
	});
	if (! found)
	{
	    current_sections = add (current_sections, current_section);
	    }
	y2milestone ("Saving section");
	if (one_section_changed)
	    sections_changed = true;

	if (getDefaultType () == `label
	    && tolower ((string)current_section_name)
		== tolower ((string)lookup_value (
		    current_globals, getDefaultKey (), "")))
	{
	    current_globals = modifySection (
		current_globals,
		getDefaultKey (),
		lookup_value (current_section, getSectionTitle (), ""));
	}
    }

    /**
      * Get map of widget
      * @return map of widget
      */
    global define map<string,any> getSingleSectionWidget () ``{
	return (map<string,any>)TablePopup::CreateTableDescr (
	    $[
		"add_delete_buttons" : true,
		"up_down_buttons" : false,
		"changed_column" : true,
		"unique_keys" : true,
	    ],
	    $[
		"store" : BootCommon::singleSectionStore,
		"options": BootCommon::cwm_options,
		"id2key": BootCommon::id2key,
		"ids": BootCommon::getTableContents,
		"help": getSingleSectionHelp (),
                "fallback" : $[
		     "init": BootCommon::commonPopupInit,
		     "store": BootCommon::commonPopupStore,
		     "summary": BootCommon::commonTableEntrySummary,
		     "changed" : BootCommon::commonTableChanged,
                ],
		"handle" : BootCommon::singleSectionHandle,
		"option_delete": BootCommon::commonTableEntryDelete,
		"add_unlisted" : false,
	    ]
	);
    }

// global section widget

    /**
      * Init function of widget
      * @param widget any id of the widget
      */
    global define void globalSectionInit (string widget) ``{
	current_section = (list<map>)(eval (globals));
	current_section_name = `global;
	TablePopup::TableInitWrapper (widget);
    }

    /**
      * Handle function of a widget
      * @param widget any widget key
      * @param event map event description of event that occured
      * @return symbol to return to wizard sequencer, or nil
      */
    global define symbol globalSectionHandle (string widget, map event) ``{
	redraw_table = false;
	redraw_dialog = false;
	map<string,any> current_table = CWM::GetProcessedWidget ();
	map options = current_bootloader_attribs["option_list"]:$[];
	current_table["add_items"] = options[`global]:[];
	symbol ret = TablePopup::TableHandle (current_table, widget, event);
	if (widget == "globalsection" && one_section_changed)
	    BootCommon::changed = true;
	if (redraw_dialog)
	{
	    return `redraw;
	}
	if (redraw_table)
	{
	    TablePopup::TableInit (current_table, widget);
	}
	return ret;
    }

    /**
      * Store function of a widget
      * @param widget any widget key
      * @param event map event description of event that occured
      */
    global define void globalSectionStore (string widget, map event) ``{
	globals = current_section;
	if (event["ID"]:nil == `redraw)
	    return;
	if (one_section_changed)
	    changed = true;
    }

    /**
      * Get map of widget
      * @return map of widget
      */
    global define map<string,any> getGlobalSectionWidget () ``{
	return (map<string,any>)TablePopup::CreateTableDescr (
	    $[
		"add_delete_buttons" : true,
		"up_down_buttons" : false,
		"changed_column" : true,
		"unique_keys" : true,
	    ],
	    $[
		"init" : BootCommon::globalSectionInit,
		"handle" : BootCommon::globalSectionHandle,
		"store" : BootCommon::globalSectionStore,
		"options": BootCommon::cwm_options,
		"id2key": BootCommon::id2key,
		"ids": BootCommon::getTableContents,
		"help": getGlobalSectionHelp (),
                "fallback" : $[
		    "init": BootCommon::commonPopupInit,
		    "store": BootCommon::commonPopupStore,
		    "summary": BootCommon::commonTableEntrySummary,
		    "changed" : BootCommon::commonTableChanged,
                ],
		"option_delete": BootCommon::commonTableEntryDelete,
		"add_unlisted" : false,
	    ]
	);
    }



    /**
      * Move item of a table up or down
      * @param opt_id any option id of selected item
      * @param opt_key any option key of selected item
      * @param dir symbol direction, `up or `down
      * @return new option id after entries have moved, or nil if no option
      *   was moved
      */
    global define any moveTableItem (any opt_id, string opt_key, symbol dir)``{
	if (! is (opt_id, integer))
	    return nil;

	integer index = (integer)opt_id;
	map reordered = filterOrder (current_section, cwm_options, index);

	if (reordered["items"]:nil == nil || reordered["index"]:nil == nil)
	    return nil;

	index = (integer)(reordered["index"]:nil);
	list items = (list)(reordered["items"]:nil);

	if (dir == `up)
	{
	    BootCommon::current_section = (list<map>)BootCommon::swapItems (
		items, index, index - 1);
	    BootCommon::one_section_changed = true;
	    if (index > 0)
		return index - 1;
	    else
		return index;
	}
	if (dir == `down)
	{
            BootCommon::current_section = (list<map>)BootCommon::swapItems (
		items, index, index + 1);
	    BootCommon::one_section_changed = true;
	    if (index + 1 < size (items))
		return index + 1;
	    else
		return index;
	}
	return nil;
    }

// reset menubutton widget

    /**
      * Init function of widget
      * @param widget any id of the widget
      */
    global define void resetButtonInit (string widget) ``{
	list items = [];
	if (getBooleanAttrib ("propose"))
	{
	    items = add (items,
		// menubutton item, keep as short as possible
		`item (`id (`propose), _("&Propose New Configuration")));
	}
	if (getBooleanAttrib ("scratch"))
	{
	    items = add (items,
		// menubutton item, keep as short as possible
		`item (`id (`scratch), _("&Start from Scratch")));
	}
        if ((Mode::normal () || Mode::config () || Mode::repair ())
	    && getBooleanAttrib ("read"))
	{
	    items = add (items,
		// menubutton item, keep as short as possible
		`item (`id (`reread), _("&Reread Configuration from Disk")));
	}
	list additional_entries = (list)getAnyTypeAttrib (
	    "additional_entries", []);
	items = merge (items, additional_entries);

	if ((Mode::normal () || Mode::repair ())
	    && getBooleanAttrib ("restore_mbr")
	    && SCR::Read (.target.size, "/boot/backup_mbr") > 0)
	{
	    items = add (items,
		// menubutton item, keep as short as possible
		`item (`id (`restore_mbr), _("Restore MBR of Hard Disk")));
	}

	if (size (items) > 0)
	{
	    UI::ReplaceWidget (`id (`_tp_table_repl),
		// menu button
		`MenuButton (`id (`reset), _("Res&et"), items));
	}
	else
	{
	    UI::ReplaceWidget (`id (`_tp_table_repl), `VSpacing (0));
	}
    }

    /**
      * Get map of widget
      * @return map of widget
      */
    global define map<string,any> getResetButtonWidget () ``{
	return $[
	    "widget" : `custom,
	    "custom_widget" : `VSpacing (0),
	    "handle" : reset_button_handle,
	    "init" : BootCommon::resetButtonInit,
	    "help" : getSummaryButtonsHelp (),
	];
    }

// manual edit button

    /**
      * Handle function of a widget
      * @param key any widget key
      * @param event map event description of event that occured
      * @return symbol to return to wizard sequencer, or nil
      */
    global define symbol manualEditHandle(string key, map event)``{
	return `manual;
    }

    /**
      * Get map of widget
      * @return map of widget
      */
    global define map<string,any> getManualEditWidget () ``{
	return $[
	    "widget" : `custom,
	    "custom_widget" : `HBox (`HStretch (),
		// pushbutton
		`PushButton (`id (`manual), _("E&dit Configuration Files")),
		`HStretch ()
	    ),
	    "handle_events" : [`manual],
	    "handle" : BootCommon::manualEditHandle,
	    "help" : getManualEditHelp (),
	];
    }

// general functions

    /**
      * Get map of dialogs and widgets
      * @return map of dialogs and widgets
      */
    global define map<string,map<string,any> > GetWidgetMaps () ``{
	return $[
	    "sections"		: BootCommon::getSectionsWidget (),
	    "singlesection"	: BootCommon::getSingleSectionWidget (),
	    "globalsection"	: BootCommon::getGlobalSectionWidget (),
	    "resetbutton"	: BootCommon::getResetButtonWidget (),
	    "manual_edit_button": BootCommon::getManualEditWidget (),
	];
    }

    /**
      * Generic version of FixSequence function
      * Just leave everything as is
      * @param ws_data map default wizard sequencer data
      * @return map updated wizard sequencer data
      */
    global define map FixSequence (map ws_data) ``{
	return ws_data;
    }

} // include end
