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 / CWM.ycp < prev    next >
Text File  |  2006-11-29  |  27KB  |  951 lines

  1. /**
  2.  * File:    modules/CWM.ycp
  3.  * Package:    Common widget manipulation
  4.  * Summary:    Routines for common widget manipulation
  5.  * Authors:    Jiri Srain <jsrain@suse.cz>
  6.  *
  7.  * $Id: CWM.ycp 29700 2006-04-05 08:53:20Z mvidner $
  8.  *
  9.  */
  10.  
  11. {
  12.  
  13.     module "CWM";
  14.     textdomain "base";
  15.  
  16.     import "Label";
  17.     import "Report";
  18.     import "Wizard";
  19.  
  20. // local variables
  21.  
  22. /**
  23.  * Widget that is being currently processed
  24.  */
  25. map<string, any> processed_widget = $[];
  26.  
  27. /**
  28.  * All widgets of the current dialog, in the correct order
  29.  */
  30. list<map<string,any> > current_dialog_widgets = [];
  31.  
  32. /**
  33.  * stack of settings of nested calls of CWM
  34.  */
  35. list<map<string,any> > settings_stack = [];
  36.  
  37. // local functions
  38.  
  39. /**
  40.  * Push the settings of the currently run dialog to the stack
  41.  */
  42. void PushSettings () {
  43.     settings_stack = prepend (settings_stack, $[
  44.     "widgets" : current_dialog_widgets,
  45.     ]);
  46. }
  47.  
  48. /**
  49.  * Pop the settings of the currently run dialog from the stack
  50.  */
  51. void PopSettings () {
  52.     map<string,any> current_dialog = settings_stack[0]:$[];
  53.     settings_stack[0] = nil;
  54.     settings_stack = filter (map<string,any> e, settings_stack, {
  55.     return e != nil;
  56.     });
  57.     current_dialog_widgets = current_dialog["widgets"]:[];
  58. }
  59.  
  60. /**
  61.  * UI containers, layout helpers that contain other widgets.  Used by
  62.  * functions that recurse through "contents" to decide whether to go
  63.  * deeper.
  64.  */
  65. list<symbol> ContainerWidgets = [
  66.     `Frame, `RadioButtonGroup,
  67.     `VBox, `HBox, `MarginBox,
  68.     `MinWidth, `MinHeight, `MinSize,
  69.     `Left, `Right, `Top, `Bottom, `HCenter, `VCenter, `HVCenter,
  70.     `HSquash, `VSquash, `HVSquash, `HWeight, `VWeight,
  71.     ];
  72.  
  73.     /**
  74.       * Process term with the dialog, replace strings in the term with
  75.       * appropriate widgets
  76.       * @param t term dialog containing strings
  77.       * @param widgets map of widget name -> widget description map
  78.       * @return term updated term ready to be used as a dialog
  79.       */
  80.     define term ProcessTerm (term t, map<string,map<string,any> > widgets) ``{
  81.     integer args = size (t);
  82.     if (args == 0)
  83.         return t;
  84.     term ret = toterm (substring (sformat ("%1", symbolof (t)), 1));
  85.     integer index = 0;
  86.     symbol current = symbolof (t);
  87.     while (index < args)
  88.     {
  89.         any arg = t[index]:nil;
  90.         if (current == `Frame && index == 0) // no action
  91.         {
  92.         y2debug ("Leaving untouched %1", arg);
  93.         }
  94.         else if (is (arg, term) && arg != nil) // recurse
  95.         {
  96.         symbol s = symbolof ((term)arg);
  97.         if (contains (ContainerWidgets, s))
  98.         {
  99.             arg = ProcessTerm ((term)arg, widgets);
  100.         }
  101.         }
  102.         else if (is (arg, string)) // action
  103.         {
  104.         arg = widgets[(string)arg, "widget"]:`VBox ();
  105.         }
  106.         ret = add (ret, arg);
  107.         index = index + 1;
  108.     }
  109.     return ret;
  110.     }
  111.  
  112.     /**
  113.       * Process term with the dialog, return all strings.
  114.       * To be used as an argument for widget_names until they are obsoleted.
  115.       * @param t term dialog containing strings
  116.       * @return strings found in the term
  117.       */
  118.     global define list<string> StringsOfTerm (term t) {
  119.     list<string> rets = [];
  120.     integer args = size (t);
  121.     integer index = 0;
  122.     while (index < args)
  123.     {
  124.         any arg = t[index]:nil;
  125.         symbol current = symbolof (t);
  126.         if (current == `Frame && index == 0) // no action
  127.         {
  128.         y2debug ("Leaving untouched %1", arg);
  129.         }
  130.         else if (is (arg, term) && arg != nil) // recurse
  131.         {
  132.         symbol s = symbolof ((term)arg);
  133.         if (contains (ContainerWidgets, s))
  134.         {
  135.             rets = rets + StringsOfTerm ((term)arg);
  136.         }
  137.         }
  138.         else if (is (arg, string)) // action
  139.         {
  140.         rets = add (rets, (string)arg);
  141.         }
  142.         index = index + 1;
  143.     }
  144.     return rets;
  145.     }
  146.  
  147. /**
  148.  * Validate the value against the basic type
  149.  * @param value any a value to validate
  150.  * @param type string type information
  151.  * @return boolean true on success or if do not know how to validate
  152.  */
  153. global boolean ValidateBasicType (any value, string type) {
  154.     if (type == "term")
  155.     return is (value, term);
  156.     if (type == "string")
  157.     return is (value, string);
  158.     if (type == "symbol")
  159.     return is (value, symbol);
  160.     if (type == "list")
  161.     return is (value, list);
  162.     if (type == "map")
  163.     return is (value, map);
  164.     if (type == "boolean")
  165.     return is (value, boolean);
  166.     if (type == "integer")
  167.     return is (value, integer);
  168.  
  169.     y2error ("Unknown value type %1", type);
  170.     return true;
  171. }
  172.  
  173.     /**
  174.       * Validate type of entry of the widget/option description map
  175.       * Also checks option description maps if present
  176.       * @param key string key of the map entry
  177.       * @param value any value of the map entry
  178.       * @param widget any name of the widget/option
  179.       * @return boolean true if validation succeeded
  180.       */
  181.     global define boolean ValidateValueType (string key, any value, string widget) {
  182.     map<string, string> types = $[
  183.         // general
  184.         "widget" : "symbol",
  185.         "custom_widget" : "term",
  186.         "handle_events" : "list",
  187.         "help" : "string",
  188.         "label" : "string",
  189.         "opt" : "list",
  190.         "ui_timeout" : "integer",
  191.         "validate_type" : "symbol",
  192.         // int field
  193.         "minimum" : "integer",
  194.         "maximum" : "integer",
  195.  
  196.         "_cwm_attrib" : "map",
  197.         "fallback" : "map",
  198.     ];
  199.     string type = (string) (types[key]:nil);
  200.     boolean success = true;
  201.     if (type == nil)
  202.     {
  203.         if (key == "widget_func")
  204.         success = is (value, term());
  205.         else if (key == "init")
  206.         success = is (value, void(string));
  207.         else if (key == "handle")
  208.         success = is (value, symbol(string,map));
  209.         else if (key == "store")
  210.         success = is (value, void(string,map));
  211.         else if (key == "cleanup")
  212.         success = is (value, void(string));
  213.         else if (key == "validate_function")
  214.         success = is (value, boolean(string,map));
  215.         else if (key == "items")
  216.         success = is (value, list<list<string> >);
  217.         else if (key == "_cwm_do_validate")
  218.         success = is (value, boolean(string,map<string,any>));
  219.     }
  220.     else
  221.     {
  222.         success = ValidateBasicType (value, type);
  223.     }
  224.  
  225.     if (! success)
  226.         y2error ("Wrong type of option %1 in description map of %2",
  227.         key, widget);
  228.  
  229.     return success;
  230.     }
  231.  
  232.     /**
  233.       * Validate value of entry of the widget/option description map
  234.       * Also checks option description maps if present
  235.       * @param key string key of the map entry
  236.       * @param value any value of the map entry
  237.       * @param widget any name of the widget/option
  238.       * @return boolean true if validation succeeded
  239.       */
  240.     define boolean ValidateValueContents (string key, any value, string widget){
  241.     string error = "";
  242.     if (key == "label")
  243.     {
  244.         string s = (string)value;
  245.         if (s == nil || size (s) == 0)
  246.         error = "Empty label";
  247.         else if (size (filterchars (s, "&")) != 1)
  248.         error = "Label has no shortcut or more than 1 shortcuts";
  249.     }
  250.     else if (key == "help")
  251.     {
  252.         string s = (string)value;
  253.         if (s == nil)
  254.         error = "Empty help";
  255.     }
  256.     else if (key == "widget")
  257.     {
  258.         symbol s = (symbol)value;
  259.         if (s == nil)
  260.         error = "No widget specified";
  261.     }
  262.     else if (key == "custom_widget")
  263.     {
  264.         term s = (term)value;
  265.         if (s == nil)
  266.         error = "No custom widget specified";
  267.     }
  268.  
  269.     if (error == "")
  270.         return true;
  271.  
  272.     y2error ("Error on key %1 of widget %2: %3", key, widget, error);
  273.     return false;
  274.     }
  275.  
  276. define integer GetLowestTimeout (list<map<string, any> > widgets) {
  277.     integer minimum = 0;
  278.     foreach (map<string,any> w, widgets, {
  279.     integer timeout = w["ui_timeout"]:0;
  280.     if ((timeout < minimum && timeout > 0) || minimum == 0)
  281.         minimum = timeout;
  282.     });
  283.     return minimum;
  284. }
  285.     /**
  286.       * Add fallback functions to a widget
  287.       * global only because of testsuites
  288.       * @param widgets a list of widget desctiption maps
  289.       * @param functions map of functions
  290.       * @return a list of modified widget description maps
  291.       */
  292.     global define list<map <string, any> > mergeFunctions (list<map <string, any> > widgets, map<any, any> functions)``{
  293.     functions = filter (any k, any v, functions, ``(is (k, string)));
  294.     map<string,any> fallback_functions = (map<string,any>) functions;
  295.     return maplist (map w, widgets, ``(
  296.         (map <string, any>)union (fallback_functions, w)
  297.     ));
  298.     }
  299.  
  300.     /**
  301.       * Set widgets according to internally stored settings
  302.       * global only because of testsuites
  303.       * @param widgets list of maps representing widgets
  304.       */
  305.     global define void initWidgets (list<map<string, any> > widgets) ``{
  306.         foreach (map<string, any> w, widgets, ``{
  307.         // set initial properties
  308.         string valid_chars = (string) w["valid_chars"]:nil;
  309.         if (valid_chars != nil)
  310.         {
  311.         UI::ChangeWidget (`id (w["_cwm_key"]:""), `ValidChars, valid_chars);
  312.         }
  313.  
  314.         // set initial values
  315.         processed_widget = w;        
  316.         void(string) toEval = (void(string)) (w["init"]:nil);
  317.         if (toEval != nil)
  318.         {
  319.         toEval (w["_cwm_key"]:"");
  320.         }
  321.         });
  322.     }
  323.  
  324.     /**
  325.       * Handle change of widget after event generated
  326.       * global only because of testsuites
  327.       * @param widgets list of maps represenging widgets
  328.       * @param event_descr map event that occured
  329.       * @return symbol modified action (sometimes may be needed) or nil
  330.       */
  331.     global define symbol handleWidgets (list<map <string, any> > widgets, map event_descr) ``{
  332.         symbol ret = nil;
  333.         foreach (map<string, any> w, widgets, ``{
  334.         if (ret == nil)
  335.         {
  336.         processed_widget = w;
  337.         list<any> events = w["handle_events"]:[];
  338.         symbol (string, map) toEval = (symbol(string, map)) (w["handle"]:nil);
  339.         if (toEval != nil && (events == [] || contains (events, (any) (event_descr["ID"]:nil))))
  340.         {
  341.                     ret = toEval (w["_cwm_key"]:"", event_descr);
  342.         }
  343.         }
  344.         });
  345.         return ret;
  346.     }
  347.  
  348.     /**
  349.       * Save changes of widget after event generated
  350.       * global only because of testsuites
  351.       * CWMTab uses it too
  352.       * @param widgets list of maps represenging widgets
  353.       * @param event map event that occured
  354.       */
  355.     global define void saveWidgets (list<map<string,any> > widgets, map event) {
  356.         foreach (map<string,any> w, widgets, ``{
  357.         processed_widget = w;
  358.             void(string, map) toEval = (void(string, map)) (w["store"]:nil);
  359.             if (toEval != nil)
  360.             {
  361.         toEval (w["_cwm_key"]:"", event);
  362.             }
  363.         });
  364.     }
  365.  
  366.     /**
  367.      * Cleanup after dialog was finished (independently on what event)
  368.      * global only because of testsuites
  369.      * @param widgets list of maps represenging widgets
  370.      */
  371.     global define void cleanupWidgets (list<map<string,any> > widgets) {
  372.         foreach (map<string,any> w, widgets, ``{
  373.         processed_widget = w;
  374.             void(string) toEval = (void(string)) (w["clean_up"]:nil);
  375.             if (toEval != nil)
  376.             {
  377.         toEval (w["_cwm_key"]:"");
  378.             }
  379.         });
  380.     }
  381.  
  382. // functions
  383.  
  384.     /**
  385.       * Return description map of currently processed widget
  386.       * @return map description map of currently processed widget
  387.       */
  388.     global define map<string, any> GetProcessedWidget () ``{
  389.     return processed_widget;
  390.     }
  391.  
  392.     /**
  393.       * Create a term with OK and Cancel buttons placed horizontally
  394.       * @return the term (HBox)
  395.       */
  396.     global define term OkCancelBox () ``{
  397.     return `HBox (
  398.         `HStretch (),
  399.         `PushButton (`id (`_tp_ok), `opt (`key_F10, `default),
  400.         Label::OKButton ()),
  401.         `HSpacing (1),
  402.         `PushButton (`id (`_tp_cancel), `opt (`key_F9),
  403.         Label::CancelButton ()),
  404.         `HStretch ()
  405.     );
  406.     }
  407.  
  408.     /**
  409.       * Validate widget description map, check for maps structure
  410.       * Also checks option description maps if present
  411.       * @param widgets map widgets description map
  412.       * @return boolean true on success
  413.       */
  414.     global define boolean ValidateMaps (map<string,map<string,any> > widgets) {
  415.     boolean ret = true;
  416.     foreach (string k, map<string,any> v, widgets, ``{
  417.         foreach (string kk, any vv, v, ``{
  418.         ret = ValidateValueType (kk, vv, k) && ret;
  419.         });
  420.         list<string> to_check = [];
  421.         if (v["widget"]:nil == `custom)
  422.         to_check = ["custom_widget"];
  423.         else if (v["widget"]:nil == `empty)
  424.         to_check = [];
  425.         else
  426.         to_check = ["label", "widget"];
  427.  
  428.         if (! haskey (v, "no_help"))
  429.         {
  430.         to_check = (list<string>)merge (to_check, ["help"]);
  431.         }
  432.         foreach (string key, to_check, ``{
  433.         if (key != "label" ||
  434.             (v["widget"]:nil != `radio_buttons
  435.             && v["widget"]:nil != `custom
  436.             && v["widget"]:nil != `func)
  437.         )
  438.         {
  439.             ret = ValidateValueContents (key, v[key]:nil, k) && ret;
  440.         }
  441.         });
  442.         if (v["widget"]:nil == `custom)
  443.         ret = ValidateValueContents ("custom_widget",
  444.             v["custom_widget"]:nil, k) && ret;
  445.  
  446.         // validate widget-specific entries
  447.         if (haskey (v, "_cwm_do_validate"))
  448.         {
  449.         boolean(string,map<string,any>) val_func
  450.            = (boolean(string,map<string,any>))v["_cwm_do_validate"]:nil;
  451.         if (val_func != nil)
  452.         {
  453.             ret = val_func (k, v) && ret;
  454.         }
  455.         }
  456.     });
  457.     return ret;
  458.     }
  459.  
  460.     /**
  461.       * Prepare a widget for usage
  462.       * @param widget_descr map widget description map
  463.       * @return map modified widget description map
  464.       */
  465.     global define map<string, any> prepareWidget (map<string, any> widget_descr) ``{
  466.     map<string, any> w = widget_descr;
  467.     symbol widget = w["widget"]:`textentry;
  468.     if (w["widget"]:nil == `empty)
  469.     {
  470.         w["widget"] = `VBox ();
  471.     }
  472.     else if (w["widget"]:nil == `custom && w["custom_widget"]:nil != nil)
  473.     {
  474.         w["widget"] = w["custom_widget"]:`VSpacing (0);
  475.     }
  476.     else if (w["widget"]:nil == `func)
  477.     {
  478.         term () toEval = (term()) (w["widget_func"]:nil);
  479.         if (toEval != nil)
  480.         {
  481.         w["widget"] = toEval ();
  482.         }
  483.         else
  484.         {
  485.         w["widget"] = `VBox ();
  486.         }
  487.     }
  488.     else
  489.     {
  490.             term id_term = `id (w["_cwm_key"]:"");
  491.         term opt_term = `opt ();
  492.         foreach (any o, w["opt"]:[], ``{
  493.             opt_term = add (opt_term, o);
  494.         });
  495.         string label = w["label"]:w["_cwm_key"]:"";
  496.  
  497.         if (widget == `textentry)
  498.         {
  499.             w["widget"] = `TextEntry (id_term, opt_term, label);
  500.         }
  501.         else if (widget == `checkbox)
  502.         {
  503.             w["widget"] = `CheckBox (id_term, opt_term, label);
  504.         }
  505.         else if (widget == `combobox)
  506.         {
  507.             w["widget"] = `ComboBox (id_term, opt_term, label,
  508.             maplist (list<string> i, w["items"]:[], ``(
  509.                 `item (`id (i[0]:""), i[1]:i[0]:"")
  510.             )));
  511.         }
  512.         else if (widget == `selection_box)
  513.         {
  514.             w["widget"] = `SelectionBox (id_term, opt_term, label,
  515.             maplist (list<string> i, w["items"]:[], ``(
  516.                 `item (`id (i[0]:""), i[1]:i[0]:"")
  517.             )));
  518.         }
  519.         else if (widget == `multi_selection_box)
  520.         {
  521.             w["widget"] = `MultiSelectionBox (id_term, opt_term, label,
  522.             maplist (list<string> i, w["items"]:[], ``(
  523.                 `item (`id (i[0]:""), i[1]:i[0]:"")
  524.             )));
  525.         }
  526.         else if (widget == `intfield)
  527.         {
  528.             integer min = w["minimum"]:0;
  529.             integer max = w["maximum"]:2147483647;
  530.             w["widget"] = `IntField (id_term, opt_term, label,
  531.             min, max, min);
  532.         }
  533.         else if (widget == `radio_buttons)
  534.         {
  535.             integer hspacing = w["hspacing"]:0;
  536.             integer vspacing = w["vspacing"]:0;
  537.             term buttons = `VBox (`VSpacing (vspacing));
  538.             foreach (list<string> i, w["items"]:[], ``{
  539.             buttons = add (buttons, `Left( `RadioButton (
  540.                 `id (i[0]:""), opt_term, i[1]:i[0]:"")) );
  541.             buttons = add (buttons, `VSpacing (vspacing));
  542.             });
  543.             w["widget"] = `Frame (
  544.             label,
  545.             `HBox (`HSpacing (hspacing),
  546.                 `RadioButtonGroup (id_term, buttons),
  547.             `HSpacing (hspacing))
  548.             );
  549.         }
  550.         else if (widget == `radio_button)
  551.         {
  552.             w["widget"] = `RadioButton (id_term, opt_term, label);
  553.         }
  554.         else if (widget == `push_button)
  555.         {
  556.             w["widget"] = `PushButton (id_term, opt_term, label);
  557.         }
  558.         else if (widget == `menu_button)
  559.         {
  560.             w["widget"] = `MenuButton (id_term, opt_term, label,
  561.             maplist (list<string> i, w["items"]:[], ``(
  562.                 `item (`id (i[0]:""), i[1]:i[0]:"")
  563.             )));
  564.         }
  565.         else if (widget == `multi_line_edit)
  566.         {
  567.             w["widget"] = `MultiLineEdit (id_term, opt_term, label);
  568.         }
  569.         else if (widget == `richtext)
  570.         {
  571.             w["widget"] = `RichText (id_term, opt_term, "");
  572.         }
  573.     }
  574.     w["custom_widget"] = nil; // not needed any more
  575.     return w;
  576.     }
  577.  
  578.     /**
  579.       * Validate single widget
  580.       * @param widget widget description map
  581.       * @param event map event that caused validation
  582.       * @param key widget key for validation by function
  583.       * @return true if validation succeeded
  584.       */
  585.     global define boolean validateWidget (map<string, any> widget, map event, string key) ``{
  586.     processed_widget = widget;
  587.     boolean failed = false;
  588.     symbol val_type = (symbol) (widget["validate_type"]:nil);
  589.     if (val_type == `function || val_type == `function_no_popup)
  590.     {
  591.         boolean (string, map) toEval = (boolean(string, map)) (widget["validate_function"]:nil);
  592.         if (toEval != nil)
  593.         {
  594.         failed = ! toEval (key, event);
  595.         }
  596.     }
  597.     else if (val_type == `regexp)
  598.     {
  599.         string regexp = (string) (widget["validate_condition"]:"");
  600.         if (! regexpmatch (
  601.         (string) UI::QueryWidget (`id (`_tp_value), `Value),
  602.         regexp))
  603.         {
  604.         failed = true;
  605.         }
  606.     }
  607.     else if (val_type == `list)
  608.     {
  609.         list possible = (list) (widget["validate_condition"]:[]);
  610.         if (! contains (
  611.         possible,
  612.         UI::QueryWidget (`id (`_tp_value), `Value)))
  613.         {
  614.         failed = true;
  615.         }
  616.     }
  617.  
  618.     if (failed && val_type != `function)
  619.     {
  620.         string error = widget["validate_help"]:"";
  621.         if (error == "")
  622.         {
  623.         string wname = widget["label"]:widget["_cwm_key"]:"";
  624.         wname = deletechars (wname, "&");
  625.         // message popup, %1 is a label of some widget
  626.         error = sformat (_("The value of %1 is invalid."), wname);
  627.         }
  628.         UI::SetFocus (`id (widget["_cwm_key"]:""));
  629.         Report::Error (error);
  630.     }
  631.     return ! failed;
  632.     }
  633.  
  634.     /**
  635.       * Validate dialog contents for allow it to be saved
  636.       * @param widgets list of widgets to validate
  637.       * @param event map event that caused validation
  638.       * @return boolean true if everything is OK, false  if something is wrong
  639.       */
  640.     global define boolean validateWidgets (list<map <string, any> > widgets, map event) ``{
  641.     boolean result = true;
  642.     foreach (map<string, any> w, widgets, ``{
  643.         string widget_key = (string)(w["_cwm_key"]:"");
  644.         result = result && validateWidget (w, event, widget_key);
  645.     });
  646.     return result;
  647.     }
  648.  
  649.     /**
  650.       * Read widgets with listed names
  651.       * @param names a list of strings/symbols names of widgets
  652.       * @param source a map containing the widgets
  653.       * @return list of maps representing widgets
  654.       */
  655.     global define list<map<string, any> > CreateWidgets (list<string> names, map<string,map<string,any> > source) ``{
  656.     ValidateMaps (source); // FIXME find better place
  657.     list<map<string,any> > ret = maplist (string w, names, ``{
  658.         map<string,any> m = source[w]:$[];
  659.         // leave add here in order to make a copy of the structure
  660.         // eval isn't usable because the map may contain terms, that can't
  661.         // be evaluated here
  662.         m = (map<string,any>)add (m, "_cwm_key", w);
  663.         return m;
  664.         });
  665.     ret = maplist (map<string, any> w, ret, ``{
  666.         return prepareWidget (w);
  667.     });
  668.     return ret;
  669.     }
  670.  
  671.  
  672.     /**
  673.       * Merge helps from the widgets
  674.       * @param widgets a list of widget description maps
  675.       * @return string merged helps of the widgets
  676.       */
  677.     global define string MergeHelps (list<map<string,any> > widgets) ``{
  678.     list<string> helps = maplist (map w, widgets, ``((string)(w["help"]:nil)));
  679.     helps = filter (string h, helps, ``(h != nil));
  680.     return mergestring (helps, "\n");
  681.     }
  682.  
  683.     /**
  684.       * Prepare the dialog, replace strings in the term with appropriate
  685.       * widgets
  686.       * @param dialog term dialog containing strings
  687.       * @param widgets list of widget description maps
  688.       * @return updated term ready to be used as a dialog
  689.       */
  690.     global define term PrepareDialog (term dialog, list<map<string,any> > widgets) ``{
  691.     integer args = size (dialog);
  692.     if (args == 0)
  693.         return dialog;
  694.     map<string,map<string,any> > m = listmap (map<string,any> w, widgets, {
  695.         string widget_key = w["_cwm_key"]:"";
  696.         return $[(string)widget_key: w];
  697.     });
  698.     return ProcessTerm (dialog, m);
  699.     }
  700.  
  701. /**
  702.  * Replace help for a particular widget
  703.  * @param widget string widget ID of widget to replace help
  704.  * @param help string new help to the widget
  705.  */
  706. global define void ReplaceWidgetHelp (string widget, string help) {
  707.     current_dialog_widgets = maplist (map<string,any> w,
  708.     current_dialog_widgets,
  709.     {
  710.     if (w["_cwm_key"]:"" == widget)
  711.     {
  712.         w["help"] = help;
  713.     }
  714.     return w;
  715.     });
  716.     string help = MergeHelps (current_dialog_widgets);
  717.     Wizard::RestoreHelp (help);
  718. }
  719.  
  720.     /**
  721.       * Generic function to create dialog and handle it's events
  722.       * @param widgets list of widget maps
  723.       * @param functions map initialize/save/handle fallbacks if not specified
  724.       *   with the widgets.
  725.       * @return symbol wizard sequencer symbol
  726.       */
  727.     global define symbol Run (list<map<string, any> > widgets, map<any, any> functions) ``{
  728.     widgets = mergeFunctions (widgets, functions);
  729.     PushSettings ();
  730.     current_dialog_widgets = widgets;
  731.     initWidgets (widgets);
  732.  
  733.     // allow a handler to enable/disable widgets before the first real
  734.     // UserInput takes place
  735.     UI::FakeUserInput ($["ID": "_cwm_wakeup"]);
  736.  
  737.     any ret = nil;
  738.     list save_exits = [`next, `ok];
  739.     boolean save = false;
  740.     map<string, any> event_descr = $[];
  741.     integer timeout = GetLowestTimeout (widgets);
  742.         while (ret != `back && ret != `abort && ! save)
  743.         {
  744.         if (timeout > 0)
  745.         {
  746.         event_descr = (map<string,any>) UI::WaitForEvent (timeout);
  747.         }
  748.         else
  749.         {
  750.         event_descr = (map<string, any>) UI::WaitForEvent ();
  751.         }
  752.         ret = (event_descr["ID"]:nil);
  753.         symbol handle_ret = handleWidgets (widgets, event_descr);
  754.         if (handle_ret != nil
  755.         || (is (ret, symbol) && contains (save_exits, ret)))
  756.         {
  757.         save = true;
  758.         if (handle_ret != nil)
  759.         {
  760.             ret = handle_ret;
  761.             event_descr["ID"] = ret;
  762.         }
  763.         }
  764.  
  765.         if (ret == `cancel)
  766.         ret = `abort;
  767.             if (ret == `abort)
  768.         {
  769.         if (functions[`abort]:nil != nil)
  770.         {
  771.             boolean () toEval = (boolean()) (functions[`abort]:nil);
  772.             if (toEval != nil)
  773.             {
  774.             boolean eval_ret = toEval ();
  775.             ret = eval_ret ? `abort : nil;
  776.             }
  777.         }
  778.         }
  779.             else if (ret == `back)
  780.         {
  781.         if (functions[`back]:nil != nil)
  782.         {
  783.             boolean () toEval = (boolean()) (functions[`back]:nil);
  784.             if (toEval != nil)
  785.             {
  786.             boolean eval_ret = toEval ();
  787.             ret = eval_ret ? `back : nil;
  788.             }
  789.         }
  790.         }
  791.  
  792.         if (ret == nil)
  793.         continue;
  794.  
  795.         if (save)
  796.         {
  797.         if (! validateWidgets (widgets, event_descr))
  798.                 ret = nil;
  799.         }
  800.  
  801.         if (ret == nil)
  802.         {
  803.         save = false;
  804.         continue;
  805.         }
  806.         }
  807.     if (save)
  808.         saveWidgets (widgets, event_descr);
  809.     cleanupWidgets (widgets);
  810.     PopSettings ();
  811.         return (symbol)ret;
  812.     }
  813.  
  814.     /**
  815.      * Disable given bottom buttons of the wizard sequencer
  816.      * @patam buttons list of buttons to be disabled
  817.      */
  818.     global define void DisableButtons (list<string> buttons) {
  819.     foreach (string button, buttons, {
  820.         if (button == "back_button")
  821.         Wizard::DisableBackButton ();
  822.         if (button == "abort_button")
  823.         Wizard::DisableAbortButton ();
  824.         if (button == "next_button")
  825.         Wizard::DisableNextButton ();
  826.     });
  827.     }
  828.     /**
  829.      * Adjust the labels of the bottom buttons of the wizard sequencer
  830.      * @param next label of the "Next" button
  831.      * @param back string label of the "Back" button
  832.      * @param abort string label of the "Abort" button
  833.      * @param help string label of the additional "Help" button (if needed)
  834.      */
  835.     global define void AdjustButtons (string next, string back, string abort,
  836.     string help)
  837.     {
  838.     if (UI::HasSpecialWidget (`Wizard))
  839.         help = "";
  840.     if (help == nil)
  841.         help = "";
  842.     if (next == nil)
  843.         next = "";
  844.     if (back == nil)
  845.         back = "";
  846.     if (abort == nil)
  847.         abort = "";
  848.     if (back == "" || help == "")
  849.     // back or help is missing, great!
  850.     {
  851.         if (next != "")
  852.         Wizard::SetNextButton (`next, next);
  853.         else
  854.         Wizard::HideNextButton ();
  855.  
  856.         if (abort != "")
  857.         Wizard::SetAbortButton (`abort, abort);
  858.         else
  859.         Wizard::HideAbortButton ();
  860.  
  861.         if (back != "")
  862.         Wizard::SetBackButton (`back, back);
  863.         else if (help != "")
  864.         Wizard::SetBackButton (`help, help);
  865.         else
  866.         Wizard::HideBackButton ();
  867.     }
  868.     else
  869.     // both back and help are present, problem!
  870.     {
  871.         // TODO this situation if it is needed
  872.     }
  873.     }
  874.  
  875.     /**
  876.      * Display the dialog and run its event loop
  877.      * @param settings a map of all settings needed to run the dialog
  878.      */
  879.     global define symbol ShowAndRun (map<string,any> settings) {
  880.     map<string,map<string,any> > widget_descr
  881.         = settings["widget_descr"]:$[];
  882.     term contents = settings["contents"]:`VBox ();
  883.     list<string> widget_names = settings["widget_names"]:StringsOfTerm (contents);
  884.     string caption = settings["caption"]:"";
  885.     string back_button = settings["back_button"]:Label::BackButton ();
  886.     string next_button = settings["next_button"]:Label::NextButton ();
  887.     string abort_button = settings["abort_button"]:Label::AbortButton ();
  888.     map<any,any> fallback = settings["fallback_functions"]:$[];
  889.  
  890.     list<map <string, any> > w = CreateWidgets (widget_names, widget_descr);
  891.     string help = MergeHelps (w);
  892.     contents = PrepareDialog (contents, w);
  893.     Wizard::SetContentsButtons (caption, contents, help,
  894.         back_button, next_button);
  895.     AdjustButtons (next_button, back_button, abort_button, nil);
  896.     DisableButtons (settings["disable_buttons"]:[]);
  897.     return Run (w, fallback);
  898.     }
  899.  
  900.     /**
  901.       * Display the dialog and run its event loop
  902.       * @param widget_names list of names of widgets that will be used in the
  903.       *   dialog
  904.       * @param widget_descr map description map of all widgets
  905.       * @param contents term contents of the dialog, identifiers instead of
  906.       *   widgets
  907.       * @param caption string dialog caption
  908.       * @param back_button string label of the back button
  909.       * @param next_button string label of the next button
  910.       * @param fallback map initialize/save/handle fallbacks if not specified
  911.       *   with the widgets.
  912.       * @return symbol wizard sequencer symbol
  913.       */
  914.     global define symbol ShowAndRunOrig (list<string> widget_names, map<string,map<string,any> > widget_descr,
  915.     term contents, string caption, string back_button, string next_button,
  916.     map<any, any> fallback)
  917.     ``{
  918.     return ShowAndRun ($[
  919.         "widget_names" : widget_names,
  920.         "widget_descr" : widget_descr,
  921.         "contents" : contents,
  922.         "caption" : caption,
  923.         "back_button" : back_button,
  924.         "next_button" : next_button,
  925.         "fallback_functions" : fallback,
  926.     ]);
  927.     }
  928.  
  929. // useful handlers
  930.  
  931.     /**
  932.      * Do-nothing replacement for a widget initialization function.
  933.      * Used for push buttons if all the other widgets have a fallback.
  934.      * @param key id of the widget
  935.      */
  936.     global void InitNull (string key) {
  937.     return;
  938.     }
  939.  
  940.     /**
  941.      * Do-nothing replacement for a widget storing function.
  942.      * Used for push buttons if all the other widgets have a fallback.
  943.      * @param key    id of the widget
  944.      * @param event    the event being handled
  945.      */
  946.     global void StoreNull (string key, map event) {
  947.     return;
  948.     }
  949.  
  950. }
  951.