home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / share / YaST2 / modules / TablePopup.ycp < prev    next >
Text File  |  2006-11-29  |  28KB  |  914 lines

  1. /**
  2.  * File:    modules/TablePopup.ycp
  3.  * Package:    Table/Popup dialogs backend
  4.  * Summary:    Routines for Table/Popup interface
  5.  * Authors:    Jiri Srain <jsrain@suse.cz>
  6.  *
  7.  * $Id: TablePopup.ycp 26946 2006-01-11 12:47:46Z jsrain $
  8.  *
  9.  */
  10.  
  11. {
  12.  
  13.     module "TablePopup";
  14.     textdomain "base";
  15.  
  16.     import "CWM";
  17.     import "Label";
  18.     import "Mode";
  19.     import "Report";
  20.  
  21.  
  22. // variables
  23.  
  24.     /**
  25.       * Item, that is the last selected
  26.       * Used to decide if selected item should be moved up or down if separator
  27.       *  clicked
  28.       * Loss of contents is no problem
  29.       */
  30.     any previous_selected_item = nil;
  31.  
  32. // local functions
  33.  
  34.     /**
  35.       * Get list of IDs of entries of the table
  36.       * @param descr map table description map
  37.       * @return list of IDs of the table
  38.       */
  39.     define list getIdList (map<string, any> descr) ``{
  40.         list(map) toEval = (list(map)) (descr["ids"]:nil);
  41.         if (toEval != nil)
  42.         {
  43.         return toEval (descr);
  44.         }
  45.     return [];
  46.     }
  47.  
  48. /**
  49.  * Validate table options specifyign attributesA
  50.  * @param attr a map of table attributes
  51.  * @return boolean true if validation succeeded
  52.  */
  53. define boolean ValidateTableAttr (map<string,any> attr) {
  54.     map<string,string> types = $[
  55.         "add_delete_buttons" : "boolean",
  56.         "edit_button" : "boolean",
  57.         "changed_column" : "boolean",
  58.         "up_down_buttons" : "boolean",
  59.     "unique_keys" : "boolean",
  60.     ];
  61.     boolean ret = true;
  62.     foreach (string k, any v, attr, {
  63.     string type = (string)types[k]:nil;
  64.     if (type == nil)
  65.     {
  66.         y2error ("Unknown attribute %1", k);
  67.         ret = false;
  68.     }
  69.     else
  70.     {
  71.         ret = CWM::ValidateBasicType (v, type) && ret;
  72.     }
  73.     });
  74.     return ret;
  75. }
  76.  
  77.  
  78. /**
  79.  * Validate type of entry of the option description map
  80.  * Also checks option description maps if present
  81.  * @param key string key of the map entry
  82.  * @param value any value of the map entry
  83.  * @param widget any name of the widget/option
  84.  * @param popup boolean true if is option of a popup
  85.  * @return boolean true if validation succeeded
  86.  */
  87. define boolean ValidateValueType (string key, any value, string widget, boolean popup) {
  88.     boolean success = true;
  89.     if (popup)
  90.     {
  91.     if (key == "init")
  92.         success = is (value, void(any,string));
  93.     else if (key == "handle")
  94.         success = is (value, void(any,string,map))
  95.         || is (value, symbol);
  96.     else if (key == "store")
  97.         success = is (value, void(any,string));
  98.     else if (key == "cleanup")
  99.         success = is (value, void(any,string));
  100.     else if (key == "validate_function")
  101.         success = is (value, boolean(any,string,map));
  102.     else if (key == "optional")
  103.         success = is (value, boolean);
  104.     else if (key == "label_func")
  105.         success = is (value, string(any,string));
  106.     else
  107.         return CWM::ValidateValueType (key, value, widget);
  108.     }
  109.     else if (key == "id2key")
  110.     success = is (value, string(map,any));
  111.     else if (key == "ids")
  112.     success = is (value, list(map));
  113.     else if (key == "option_delete")
  114.     success = is (value, boolean(any,string));
  115.     else if (key == "summary")
  116.     success = is (value, string(any,string));
  117.     else if (key == "label_func")
  118.     success = is (value, string(any,string));
  119.     else if (key == "option_move")
  120.     success = is (value, any(any, string, symbol));
  121.     else if (key == "options")
  122.     success = is (value, map<string,any>);
  123.     else if (key == "add_items")
  124.     success = is (value, list);
  125.  
  126.     if (! success)
  127.     y2error ("Wrong type of option %1 in description map of %2",
  128.         key, widget);
  129. }
  130.  
  131. /**
  132.  * Validate the table description
  133.  * @param descr a map containing the table description
  134.  * @return boolean true if validation succeeded
  135.  */
  136. define boolean ValidateTableDescr (string key, map<string,any> descr) {
  137.     boolean ret = true;
  138.     foreach (string k, any v, descr, {
  139.     ret = ValidateValueType (k, v, key, false) && ret;
  140.     });
  141.     map<string,any> options = descr["options"]:$[];
  142.     foreach (string w_key, any v, options, {
  143.     map<string,map<string,any> > des = (map<string,map<string,any> >)v;
  144.     foreach (string group, map<string,any> d, des, {
  145.         if (group != "table" && group != "popup")
  146.         {
  147.         y2error ("Unknown entry in option %1: %2", w_key, group);
  148.         }
  149.         foreach (string key, any value, d, {
  150.         ValidateValueType (key, value, w_key, true);
  151.         });
  152.     });
  153.     });
  154.     return ret;
  155. }
  156.  
  157.     /**
  158.       * Get option key from the option id
  159.       * global only because of testsuites
  160.       * @param descr map description of the table
  161.       * @param opt_id any id of the option
  162.       * @return string option key
  163.       */
  164.     global define string id2key (map<string, any> descr, any opt_id) ``{
  165.     if (opt_id != nil && is (opt_id, string) && size ((string)opt_id) >= 7
  166.         && substring ((string)opt_id, 0, 7) == "____sep")
  167.     {
  168.         return "____sep";
  169.     }
  170.     string (map, any) toEval = (string(map, any)) (descr["id2key"]:nil);
  171.     if (toEval != nil)
  172.         return toEval (descr, opt_id);
  173.     else
  174.         return (string)opt_id;
  175.     }
  176.  
  177.     /**
  178.       * Get option description map from the key
  179.       * global only because of testsuites
  180.       * @param descr map description of the table
  181.       * @param opt_key string option key
  182.       * @return map option description map
  183.       */
  184.     global define map<string, any> key2descr (map<string, any> descr, string opt_key) ``{
  185.     map options = (map)(descr["options"]:$[]);
  186.     map<string, any> opt_descr = (map<string, any>)(options[opt_key]:$[]);
  187.     // a copy wanted here
  188.     opt_descr = (map<string, any>) add (opt_descr, "_cwm_key", opt_key);
  189.     // a deep copy
  190.     opt_descr["table"] = add ((map)(opt_descr["table"]:$[]), "_cwm_key", opt_key);
  191.     opt_descr["popup"] = add ((map)(opt_descr["popup"]:$[]), "_cwm_key", opt_key);
  192.     if (opt_descr["popup", "label"]:nil == nil)
  193.         opt_descr["popup", "label"] = opt_descr["table", "label"]:opt_key;
  194.     return opt_descr;
  195.     }
  196.  
  197.     /**
  198.       * Update the option description map in order to contain handlers of
  199.       *  all needed functions
  200.       * global only because of testsuites
  201.       * @param opt_descr map option description map
  202.       * @param fallbacks map of fallback handlers
  203.       * @return map updated option description map
  204.       */
  205.     global define map<string, any> updateOptionMap (map<string, any> opt_descr, map fallbacks) ``{
  206.     // ensure that the submaps exist
  207.     opt_descr["table"] = opt_descr["table"]:$[];
  208.     opt_descr["popup"] = opt_descr["popup"]:$[];
  209.     foreach (string k, ["init", "store" ], ``{
  210.         if (! haskey (opt_descr["popup"]:$[], k) && haskey (fallbacks, k))
  211.         opt_descr["popup", k] = fallbacks[k]:nil;
  212.     });
  213.     if (! haskey (opt_descr["table"]:$[], "summary")
  214.         && haskey (fallbacks, "summary"))
  215.     {
  216.         opt_descr["table", "summary"] = fallbacks["summary"]:nil;
  217.     }
  218.     if (! haskey (opt_descr["table"]:$[], "label_func")
  219.         && haskey (fallbacks, "label_func"))
  220.     {
  221.         opt_descr["table", "label_func"] = fallbacks["label_func"]:nil;
  222.     }
  223.     if (! haskey (opt_descr["table"]:$[], "changed")
  224.         && haskey (fallbacks, "changed"))
  225.     {
  226.         opt_descr["table", "changed"] = fallbacks["changed"]:nil;
  227.     }
  228.     if (opt_descr["_cwm_key"]:"" == "____sep"
  229.         && opt_descr["table", "label"]:"" == "")
  230.     {
  231.         opt_descr["table", "label"] = "--------------------";
  232.     }
  233.     return opt_descr;
  234.     }
  235.  
  236.     /**
  237.       * Get the left column of the table
  238.       * @param opt_id any option id
  239.       * @param opt_descr map option description map
  240.       * @return string text to the table
  241.       */
  242.     define string tableEntryKey (any opt_id, map<string, any> opt_descr) ``{
  243.     string opt_key = opt_descr["_cwm_key"]:"";
  244.     string label = opt_descr["table", "label"]:sformat ("%1", opt_key);
  245.     if (haskey (opt_descr["table"]:$[], "label_func"))
  246.     {
  247.         string(any,string) label_func = (string(any,string))
  248.         opt_descr["table", "label_func"]:nil;
  249.         label = label_func (opt_id, opt_key);
  250.     }
  251.     return label;
  252.     }
  253.  
  254.     /**
  255.       * Get value to the table entry
  256.       * @param opt_id any option id
  257.       * @param opt_descr map option description map
  258.       * @return string text to the table
  259.       */
  260.     define string tableEntryValue (any opt_id, map<string, any> opt_descr) ``{
  261.     string opt_key = (string)(opt_descr["_cwm_key"]:"");
  262.     string (any, string) toEval = (string(any, string)) (opt_descr["table", "summary"]:nil);
  263.     if (toEval != nil)
  264.     {
  265.         return toEval (opt_id, opt_key);
  266.     }
  267.     return "";
  268.     }
  269.  
  270.     /**
  271.       * Realize if table entry was changed
  272.       * @param opt_id any option id
  273.       * @param opt_descr map option description map
  274.       * @return boolean true if was changed
  275.       */
  276.     global define boolean tableEntryChanged (any opt_id, map<string, any> opt_descr) ``{
  277.     string opt_key = (string)(opt_descr["_cwm_key"]:"");
  278.     boolean (any, string) toEval = (boolean(any, string)) (opt_descr["table", "changed"]:nil);
  279.     if (toEval != nil)
  280.     {
  281.         return toEval (opt_id, opt_key);
  282.     }
  283.     return false;
  284.     }
  285.  
  286.     /**
  287.       * Delete an item from the table
  288.       * Just a wrapper for module-specific function
  289.       * @param opt_id any option id
  290.       * @param descr map table description map
  291.       * @return boolean true if was really deleted
  292.       */
  293.     global define boolean deleteTableItem (any opt_id, map<string, any> descr) ``{
  294.     boolean(any, string) toEval = (boolean(any,string)) (descr["option_delete"]:nil);
  295.     if (nil != toEval)
  296.     {
  297.         string opt_key = id2key (descr, opt_id);
  298.         return toEval (opt_id, opt_key);
  299.     }
  300.     return false;
  301.    }
  302.  
  303.     /**
  304.       * Enable or disable the Delete and up/down buttons
  305.       * @param descr map table description map
  306.       * @param opt_descr map selected option description map
  307.       */
  308.     global define void updateButtons (map<string,any> descr, map<string,any> opt_descr) ``{
  309.     if (descr["_cwm_attrib", "add_delete_buttons"]:true)
  310.     {
  311.         UI::ChangeWidget (`id (`_tp_delete), `Enabled,
  312.         opt_descr["table", "optional"]:true);
  313.     }
  314.     if (descr["_cwm_attrib", "edit_button"]:true)
  315.     {
  316.         UI::ChangeWidget (`id (`_tp_edit), `Enabled,
  317.         ! opt_descr["table", "immutable"]:false);
  318.     }
  319.     if (descr["_cwm_attrib", "up_down_buttons"]:false)
  320.     {
  321.         UI::ChangeWidget (`id (`_tp_up), `Enabled,
  322.         opt_descr["table", "ordering"]:true);
  323.         UI::ChangeWidget (`id (`_tp_down), `Enabled,
  324.         opt_descr["table", "ordering"]:true);
  325.     }
  326.     }
  327.  
  328.     /**
  329.       * Move table item up or down
  330.       * Just a wrapper for module-specific function
  331.       * @param opt_id any option id
  332.       * @param descr map table description map
  333.       * @param dir symbol `up or `down (according to the button user pressed)
  334.       * @return any new id of selected option, nil if wasn't reordered
  335.       */
  336.     define any moveTableItem (any opt_id, map<string, any> descr, symbol dir) ``{
  337.     any(any, string, symbol) toEval = (any(any, string, symbol)) (descr["option_move"]:nil);
  338.     if (nil != toEval)
  339.     {
  340.         return toEval (opt_id, id2key (descr, opt_id), dir);
  341.     }
  342.     return nil;
  343.     }
  344.  
  345.     /**
  346.       * Redraw completely the table
  347.       * @param descr map description map of the whole table
  348.       * @param update_buttons boolean true if buttons status (enabled/disabled)
  349.       *  should be updated according to currently selected item
  350.       */
  351.     define void TableRedraw (map<string, any> descr, boolean update_buttons) ``{
  352.     list id_list = getIdList (descr);
  353.     if (previous_selected_item == nil)
  354.     {
  355.         previous_selected_item = id_list[0]:nil;
  356.     }
  357.     list<term> entries = maplist (any opt_id, id_list, ``{
  358.         string opt_val = "";
  359.         string val = "";
  360.         boolean opt_changed = false;
  361.         string opt_key = id2key (descr, opt_id);
  362.         map<string, any> opt_descr = key2descr (descr, opt_key);
  363.         opt_descr = updateOptionMap (opt_descr, descr["fallback"]:$[]);
  364.         string label = tableEntryKey (opt_id, opt_descr);
  365.         if (opt_key != "____sep")
  366.         {
  367.         opt_val = tableEntryValue (opt_id, opt_descr);
  368.         opt_changed = tableEntryChanged (opt_id, opt_descr);
  369.         }
  370.         if (update_buttons && opt_id == previous_selected_item)
  371.         updateButtons (descr, opt_descr);
  372.         if (descr["_cwm_attrib", "changed_column"]:false)
  373.         {
  374.         return (`item (
  375.             `id (opt_id),
  376.             opt_changed ? "*" : "",
  377.             label,
  378.             sformat ("%1", opt_val)));
  379.  
  380.         }
  381.         return (`item (
  382.         `id (opt_id),
  383.         label,
  384.         sformat ("%1", opt_val)));
  385.     });
  386.     UI::ChangeWidget (`id (`_tp_table), `Items, entries);
  387.     UI::SetFocus (`id (`_tp_table));
  388.     }
  389.  
  390.     /**
  391.       * Displaye popup for option to edit choosing
  392.       * @param possible a list of strings or items of all possible options
  393.       *   to provide
  394.       * @param editable boolean true means that it is possible to add non-listed
  395.       *   options
  396.       * @param descr a map table description map
  397.       * @return string option identifies, nil if canceled
  398.       */
  399.     global define string askForNewOption (list possible, boolean editable, map<string,any> descr) ``{
  400.     boolean do_sort = ! descr["add_items_keep_order"]:false;
  401.     if (do_sort)
  402.         possible = sort (possible);
  403.     map<string,string> val2key = $[];
  404.     map<string,boolean> known_keys = $[];
  405.     possible = maplist (any p, possible, ``{
  406.         if (!is (p, string)) continue;
  407.         map<string, any> opt_descr = key2descr (descr, (string)p);
  408.         string label = opt_descr["table", "label"]:sformat ("%1", p);
  409.         known_keys[(string)p] = true;
  410.         val2key[label] = (string)p;
  411.         return `item (`id (p), label);
  412.     });
  413.     term widget = `HBox (`HSpacing (1), `VBox (
  414.         `VSpacing (1),
  415.         `ComboBox (`id (`optname), editable ? `opt (`editable) : `opt (),
  416.         // combobox header
  417.         _("&Selected Option"), possible),
  418.         `VSpacing (1),
  419.         `HBox (
  420.             `HStretch (),
  421.             `PushButton (`id (`_tp_ok), `opt (`key_F10, `default),
  422.             Label::OKButton ()),
  423.         `HSpacing (1),
  424.         `PushButton (`id (`_tp_cancel), `opt (`key_F9),
  425.             Label::CancelButton ()),
  426.         `HStretch ()
  427.         ),
  428.         `VSpacing (1)
  429.     ), `HSpacing (1));
  430.     UI::OpenDialog (widget);
  431.     UI::SetFocus (`id (`optname));
  432.     any ret = nil;
  433.     string option = nil;
  434.     while (ret != `_tp_ok && ret != `_tp_cancel)
  435.     {
  436.         ret = UI::UserInput ();
  437.         if (ret == `_tp_ok)
  438.         {
  439.         option = (string)(UI::QueryWidget (`id (`optname), `Value));
  440.         }
  441.     }
  442.     UI::CloseDialog ();
  443.     if (ret == `_tp_cancel)
  444.         return nil;
  445.     if (known_keys[option]:false)
  446.         return option;
  447.     return val2key[option]:option;
  448.     }
  449.  
  450.     /**
  451.       * Display and handle the popup for option
  452.       * @param option map one option description map that is modified in order
  453.       *   to contain the option name and more percise option identification
  454.       * @return symbol `_tp_ok or `_tp_cancel
  455.       */
  456.     global define symbol singleOptionEditPopup (map<string,any> option)``{
  457.     string opt_key = option["_cwm_key"]:"";
  458.     any opt_id = option["_cwm_id"]:nil;
  459.  
  460.     string label = sformat ("%1", option["table", "label"]:opt_key);
  461.     term header = `HBox (
  462.         // heading / label
  463.         `Heading (_("Current Option: ")),
  464.         `Label (label),
  465.         `HStretch ()
  466.     );
  467.     map<string, any> popup_descr = CWM::prepareWidget (option["popup"]:$[]);
  468.     term widget = (term)(popup_descr["widget"]:`VBox ());
  469.     string help = (string)(popup_descr["help"]:"");
  470.     if (help == nil)
  471.     {
  472.         help = "";
  473.     }
  474.     term contents = `HBox (`HSpacing (1), `VBox (
  475.         `VSpacing (1),
  476.         `Left (header),
  477.         `VSpacing (1),
  478.         help == "" ? `VSpacing (0)
  479.         : `Left (`Label (help)),
  480.         `VSpacing (help == "" ? 0 : 1),
  481.         `Left (`ReplacePoint(`id (`value_rp), widget)),
  482.         `VSpacing (1),
  483.         `HBox (
  484.         `HStretch(),
  485.         `PushButton (`id (`_tp_ok), `opt (`key_F10, `default),
  486.             Label::OKButton ()),
  487.         `HSpacing (1),
  488.         `PushButton (`id (`_tp_cancel), `opt (`key_F9),
  489.             Label::CancelButton ()),
  490.         `HStretch ()
  491.         ),
  492.         `VSpacing (1)
  493.     ), `HSpacing (1));
  494.     UI::OpenDialog (contents);
  495.     if (popup_descr["init"]:nil != nil)
  496.     {
  497.         void(any, string) toEval = (void(any, string)) (popup_descr["init"]:nil);
  498.         toEval (opt_id, opt_key);
  499.     }
  500.     any ret = nil;
  501.     map<string, any> event_descr = $[];
  502.     while (ret != `_tp_ok && ret != `_tp_cancel)
  503.     {
  504.         map<string, any> event_descr = (map<string, any>)(UI::WaitForEvent ());
  505.         if (Mode::test ())
  506.         event_descr = $[ "ID" : `_tp_ok];
  507.         ret = event_descr["ID"]:nil;
  508.         if (popup_descr["handle"]:nil != nil)
  509.         {
  510.         void(any, string, map) toEval = (void(any, string, map)) (popup_descr["handle"]:nil);
  511.         toEval (opt_id, opt_key, event_descr);
  512.         }
  513.         if (ret == `_tp_ok)
  514.         {
  515.         symbol val_type = (symbol) (popup_descr["validate_type"]:nil);
  516.         if (val_type == `function)
  517.         {
  518.             boolean (any, string, map) toEval
  519.             = (boolean(any, string, map)) (popup_descr["validate_function"]:nil);
  520.             if (toEval != nil)
  521.             {
  522.             if (! toEval (opt_id, opt_key, event_descr))
  523.             {
  524.                 ret = nil;
  525.             }
  526.             }
  527.         }
  528.         else if (! CWM::validateWidget (popup_descr, event_descr,
  529.             opt_key))
  530.         {
  531.             ret = nil;
  532.         }
  533.         }
  534.     }
  535.     if (ret == `_tp_ok && popup_descr["store"]:nil != nil)
  536.     {
  537.         void(any, string) toEval = (void(any, string)) (popup_descr["store"]:nil);
  538.         toEval (opt_id, opt_key);
  539.     }
  540.  
  541.     UI::CloseDialog ();
  542.     return (symbol)ret;
  543.     }
  544.  
  545.  
  546.  
  547.  
  548. // functions
  549.  
  550.     /**
  551.       * Disable whole table
  552.       * @param descr map table widget description map
  553.       */
  554.     global define void DisableTable (map<string,any> descr) ``{
  555.     UI::ChangeWidget (`id (`_tp_table), `Enabled, false);
  556.     if (descr["_cwm_attrib", "edit_button"]:true)
  557.     {
  558.         UI::ChangeWidget (`id (`_tp_edit), `Enabled, false);
  559.     }
  560.     if (descr["_cwm_attrib", "add_delete_buttons"]:true)
  561.     {
  562.         UI::ChangeWidget (`id (`_tp_delete), `Enabled, false);
  563.         UI::ChangeWidget (`id (`_tp_add), `Enabled, false);
  564.     }
  565.     if (descr["_cwm_attrib", "up_down_buttons"]:false)
  566.     {
  567.         UI::ChangeWidget (`id (`_tp_up), `Enabled, false);
  568.         UI::ChangeWidget (`id (`_tp_down), `Enabled, false);
  569.     }
  570.     }
  571.  
  572.     /**
  573.       * Enable whole table (except buttons that should be grayed according to
  574.       * currently selected table row
  575.       * @param descr map table widget description map
  576.       */
  577.     global define void EnableTable (map<string, any> descr) ``{
  578.     UI::ChangeWidget (`id (`_tp_table), `Enabled, true);
  579.     if (descr["_cwm_attrib", "edit_button"]:true)
  580.     {
  581.         UI::ChangeWidget (`id (`_tp_edit), `Enabled, true);
  582.     }
  583.     if (descr["_cwm_attrib", "add_delete_buttons"]:true)
  584.     {
  585.         UI::ChangeWidget (`id (`_tp_add), `Enabled, false);
  586.     }
  587.  
  588.     any opt_id = UI::QueryWidget (`id (`_tp_table), `CurrentItem);
  589.     string opt_key = id2key (descr, opt_id);
  590.     map<string,any> option_map = key2descr (descr, opt_key);
  591.     updateButtons (descr, option_map);
  592.     }
  593.  
  594.     /**
  595.       * Initialize the displayed table
  596.       * @param descr map description map of the whole table
  597.       * @param key table widget key
  598.       */
  599.     global define void TableInit (map<string, any> descr, string key) ``{
  600.     previous_selected_item = nil;
  601.     descr["_cwm_key"] = key;
  602.     TableRedraw (descr, true);
  603.     }
  604.  
  605.     /**
  606.       * Handle the event that happened on the table
  607.       * @param descr map description of the table
  608.       * @param key table widget key
  609.       * @param event_descr map event to handle
  610.       * @return symbol modified event if needed
  611.       */
  612.     global define symbol TableHandle (map<string, any> descr, string key, map event_descr) ``{
  613.     any event_id = event_descr["ID"]:nil;
  614.     UI::SetFocus (`id (`_tp_table));
  615.     if (event_id == `_tp_table)
  616.     {
  617.         if (event_descr["EventReason"]:"" == "Activated"
  618.         && event_descr["EventType"]:"" == "WidgetEvent"
  619.         && UI::WidgetExists (`id (`_tp_edit)))
  620.         {
  621.         event_id = `_tp_edit;
  622.         }
  623.     }
  624.         if (event_id == `_tp_edit || event_id == `_tp_add)
  625.         {
  626.             string opt_key = nil;
  627.             any opt_id = nil;
  628.  
  629.             if (event_id == `_tp_add)
  630.             {
  631.         boolean add_unlisted = descr["add_unlisted"]:true;
  632.         if (! add_unlisted && size (descr["add_items"]:[]) == 1)
  633.         {
  634.             opt_key = descr["add_items", 0]:"";
  635.         }
  636.         else
  637.         {
  638.             list<string> add_opts = descr["add_items"]:[];
  639.             list ids = getIdList (descr);
  640.             list<string> present = maplist (any i, ids, ``(
  641.             id2key (descr, i)
  642.             ));
  643.             if (! descr["_cwm_attrib", "unique_keys"]:false)
  644.             {
  645.             present = filter (string i, present, ``{
  646.                 map opt_descr = key2descr (descr, i);
  647.                 return ! opt_descr["table", "optional"]:true;
  648.             });
  649.             }
  650.             add_opts = filter (string o, add_opts, ``(
  651.             ! contains (present, o)
  652.             ));
  653.             boolean selected = false;
  654.             while (! selected)
  655.             {
  656.             opt_key = askForNewOption (add_opts, add_unlisted,
  657.                 descr);
  658.             if (opt_key == nil)
  659.                 return nil;
  660.             if (contains (present, opt_key))
  661.                 Report::Error (
  662.                 // error report
  663.                 _("The selected option is already present."));
  664.             else
  665.                 selected = true;
  666.             }
  667.  
  668.         }
  669.                 if (opt_key == nil)
  670.                     return nil;
  671.             }
  672.             else if (event_id == `_tp_edit)
  673.             {
  674.                 opt_id = UI::QueryWidget (`id (`_tp_table), `CurrentItem);
  675.                 opt_key = id2key (descr, opt_id);
  676.             }
  677.             map<string, any> option_map = key2descr (descr, opt_key);
  678.         any toEval = option_map["table", "handle"]:nil;
  679.         if (toEval != nil)
  680.         {
  681. //        if (is (toEval, symbol))
  682.         if (! is (toEval, symbol(any, string, map)))
  683.         {
  684.             symbol ret = (symbol)toEval;
  685.             return ret;
  686.         }
  687.         else
  688.         {
  689.             symbol(any, string, map) toEval_c
  690.             = (symbol(any, string, map)) (option_map["table", "handle"]:nil);
  691.             symbol ret = toEval_c (opt_id, opt_key, event_descr);
  692.             if (ret != `_tp_normal)
  693.             return ret;
  694.         }
  695.         }
  696.             option_map["_cwm_id"] = opt_id;
  697.         option_map["_cwm_key"] = opt_key;
  698.         // add generic handlers if needed
  699.         option_map = updateOptionMap (option_map, (map<string, any>) (descr["fallback"]:$[]));
  700.             symbol ret = singleOptionEditPopup (option_map);
  701.             if (ret == `_tp_ok)
  702.             {
  703.                 if (event_id == `_tp_add)
  704.                 {
  705.                     TableInit (descr, key);
  706.                 }
  707.                 else if (event_id == `_tp_edit)
  708.                 {
  709.             integer column
  710.             = descr["_cwm_attrib", "changed_column"]:false
  711.                 ? 2
  712.                 : 1;
  713.             UI::ChangeWidget (`id (`_tp_table),
  714.             `Item (opt_id, column),
  715.             tableEntryValue (opt_id, option_map));
  716.             // also redraw the key field as it can be changed
  717.             column = column - 1;
  718.             UI::ChangeWidget (`id (`_tp_table),
  719.             `Item (opt_id, column),
  720.             tableEntryKey (opt_id, option_map));
  721.             if (descr["_cwm_attrib", "changed_column"]:false)
  722.             {
  723.              UI::ChangeWidget (`id (`_tp_table),
  724.                 `Item (opt_id, 0),
  725.                 tableEntryChanged (opt_id, option_map) ? "*" : "");
  726.             }
  727.                 }
  728.             }
  729.         }
  730.     else if (event_id == `_tp_delete)
  731.     {
  732.         any opt_id = UI::QueryWidget (`id (`_tp_table), `CurrentItem);
  733.         if (deleteTableItem (opt_id, descr))
  734.         TableInit (descr, key);
  735.     }
  736.     else if (event_id == `_tp_table)
  737.     {
  738.         any opt_id = UI::QueryWidget (`id (`_tp_table), `CurrentItem);
  739.         any key = id2key (descr, opt_id);
  740.         if (key == "____sep")
  741.         {
  742.         list id_list = getIdList (descr);
  743.         integer previous_index = 0;
  744.         if (previous_selected_item != nil)
  745.         {
  746.             previous_index = -1;
  747.             find (any e, id_list, ``{
  748.             previous_index = previous_index + 1;
  749.             return e == previous_selected_item;
  750.             });
  751.         }
  752.         integer current_index = -1;
  753.         find (any e, id_list, ``{
  754.             current_index = current_index + 1;
  755.             return e == opt_id;
  756.         });
  757.         integer step = 0;
  758.         if (current_index == 0)
  759.             step = 1;
  760.         else if (current_index + 1 == size (id_list))
  761.             step = -1;
  762.         else if (current_index >= previous_index)
  763.             step = 1;
  764.         else
  765.             step = -1;
  766.         integer new_index = current_index + step;
  767.         opt_id = id_list[new_index]:nil;
  768.         key = id2key (descr, opt_id);
  769.         UI::ChangeWidget (`id (`_tp_table), `CurrentItem, opt_id);
  770.         }
  771.         previous_selected_item = opt_id;
  772.         map<string,any> opt_descr
  773.         = key2descr (descr, id2key (descr, opt_id));
  774.         updateButtons (descr, opt_descr);
  775.     }
  776.     else if (event_id == `_tp_up || event_id == `_tp_down)
  777.     {
  778.         any opt_id = UI::QueryWidget (`id (`_tp_table), `CurrentItem);
  779.         opt_id = moveTableItem (opt_id, descr, event_id == `_tp_up
  780.         ? `up
  781.         : `down);
  782.         if (nil != opt_id)
  783.         {
  784.         TableRedraw (descr, false);
  785.         UI::ChangeWidget (`id (`_tp_table), `CurrentItem, opt_id);
  786.         map<string,any> opt_descr
  787.             = key2descr (descr, id2key (descr, opt_id));
  788.         updateButtons (descr, opt_descr);
  789.         }
  790.     }
  791.     return nil;
  792.     }
  793.  
  794.     /**
  795.       * Wrapper for TableInit using CWM::GetProcessedWidget () for getting
  796.       * widget description map
  797.       * @param key any widget key
  798.       */
  799.     global define void TableInitWrapper (string key) ``{
  800.     TableInit (CWM::GetProcessedWidget (), key);
  801.     }
  802.  
  803.     /**
  804.       * Wrapper for TableHandle using CWM::GetProcessedWidget () for getting
  805.       * widget description map
  806.       * @param key any widget key
  807.       * @param event_descr map event description map
  808.       * @return symbol return value for wizard sequencer or nil
  809.       */
  810.     global define symbol TableHandleWrapper (string key, map event_descr) ``{
  811.     return TableHandle(CWM::GetProcessedWidget (), key, event_descr);
  812.     }
  813.  
  814.     /**
  815.       * Get the map with the table widget
  816.       * @param attrib map table attributes
  817.       * @param widget_descr map widget description map of the table, will be
  818.       *  unioned with the generated map
  819.       * @return map table widget
  820.       */
  821.     global define map<string,any> CreateTableDescr (map<string,any> attrib, map<string,any> widget_descr) ``{
  822.     ValidateTableAttr (attrib);
  823.     term add_button = attrib["add_delete_buttons"]:true
  824.         ? `PushButton (`id (`_tp_add), `opt (`key_F3), Label::AddButton ())
  825.         : `HSpacing (0);
  826.     term edit_button = attrib["edit_button"]:true
  827.         ? `PushButton (`id (`_tp_edit), `opt (`key_F4), Label::EditButton ())
  828.         : `HSpacing (0);
  829.     term delete_button = attrib["add_delete_buttons"]:true
  830.         ? `PushButton (`id (`_tp_delete), `opt (`key_F5), Label::DeleteButton ())
  831.         : `HSpacing (0);
  832.     term table_header = attrib["changed_column"]:false
  833.         ? `header (
  834.         // table header, shortcut for changed, keep very short
  835.         _("Ch."),
  836.         // table header
  837.         _("Option"),
  838.         // table header
  839.         _("Value"))
  840.         : `header (
  841.         // table header
  842.         _("Option"),
  843.         // table header
  844.         _("Value"));
  845.  
  846.     term replace_point = `ReplacePoint (`id (`_tp_table_repl), `HSpacing (0));
  847.     // help 1/4
  848.     string help = _("<p><b><big>Editing the Settings</big></b><br>
  849. To edit the settings, choose the appropriate
  850. entry of the table then click <b>Edit</b>.</p>");
  851.     if (attrib["add_delete_buttons"]:true)
  852.         // help 2/4, optional
  853.         help = help + _("<p>To add a new option, click <b>Add</b>. To remove
  854. an option, select it and click <b>Delete</b>.</p>");
  855.  
  856.     if (attrib["changed_column"]:false)
  857.         // help 3/4, optional
  858.         help = help + _("<P>The <B>Ch.</B> column of the table shows 
  859. whether the option was changed.</P>");
  860.  
  861.     if (attrib["up_down_buttons"]:false)
  862.         // help 4/4, optional
  863.         help = help + _("<p>To reorder the options, select an option
  864. and use <b>Up</b> and <b>Down</b> to move it up or down
  865. in the list.</p>");
  866.  
  867.     term up_down = attrib["up_down_buttons"]:false
  868.         ? `VBox (
  869.         `VStretch (),
  870.         // push button
  871.         `PushButton (`id (`_tp_up), _("&Up")),
  872.         // push button
  873.         `PushButton (`id (`_tp_down), _("&Down")),
  874.         `VStretch ()
  875.         )
  876.         : `HSpacing (0);
  877.  
  878.     map<string,any> ret = (map<string,any>)union ($[
  879.             "custom_widget" : (`HBox (`HSpacing (2), `VBox (
  880.             `HBox (
  881.                     `Table (`id (`_tp_table),
  882.                 `opt (`immediate, `notify, `keepSorting),
  883.                 table_header,
  884.                      []),
  885.             up_down
  886.             ),
  887.                     `HBox (
  888.             add_button,
  889.             edit_button,
  890.             delete_button,
  891.                         `HStretch (),
  892.             replace_point
  893.                     )
  894.                 ), `HSpacing (2))),
  895.         "_cwm_attrib" : attrib,
  896.         "widget" : `custom,
  897.         "help" : help,
  898.         "_cwm_do_validate" : ValidateTableDescr,
  899.     ], widget_descr);
  900.  
  901.     if (! haskey (ret, "init"))
  902.     {
  903.         ret["init"] = TablePopup::TableInitWrapper;
  904.     }
  905.     if (! haskey (ret, "handle"))
  906.     {
  907.         ret["handle"] = TablePopup::TableHandleWrapper;
  908.     }
  909.  
  910.     return ret;
  911.     }
  912.  
  913. }
  914.