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 / LogView.ycp < prev    next >
Text File  |  2006-11-29  |  21KB  |  747 lines

  1. /**
  2.  * File:    modules/LogView.ycp
  3.  * Package:    YaST2
  4.  * Summary:    Displaying a log with additional functionality
  5.  * Authors:    Jiri Srain <jsrain@suse.cz>
  6.  *
  7.  * $Id: LogView.ycp 18863 2004-08-27 13:14:34Z arvin $
  8.  *
  9.  * All of these functions watch the log file and display
  10.  * added lines as the log grows.
  11.  * <pre>
  12.  * LogView::DisplaySimple ("/var/log/messages");
  13.  * LogView::DisplayFiltered ("/var/log/messages", "\\(tftp\\|TFTP\\)");
  14.  * LogView::Display ($[
  15.  *          "file" : "/var/log/messages",
  16.  *          "grep" : "dhcpd",
  17.  *          "save" : true,
  18.  *          "actions" : [    // menu buttons
  19.  *              [ _("Restart DHCP Server"),
  20.  *                  RestartDhcpDaemon ],
  21.  *              [ _("Save Settings and Restart DHCP Server"),
  22.  *                  DhcpServer::Write ],
  23.  *          ],
  24.  *      ]);
  25.  * </pre>
  26.  */
  27.  
  28. {
  29.  
  30. module "LogView";
  31. textdomain "base";
  32.  
  33. import "CWM";
  34. import "Popup";
  35. import "Label";
  36. import "Report";
  37.  
  38. // fallback settings variables
  39.  
  40. /**
  41.  * default value of maximum displayed lines
  42.  */
  43. integer max_lines_default = 100;
  44.  
  45.  
  46. // configuration variables
  47.  
  48. /**
  49.  * global parameters for the log displaying widget
  50.  */
  51. map<string,any> param = $[];
  52.  
  53. /**
  54.  * list of all the logs that can be displayed
  55.  */
  56. list<map<string,any> > logs = [];
  57.  
  58. /**
  59.  * index of currently selected log
  60.  */
  61. integer current_index = 0;
  62.  
  63. /**
  64.  * list of actions that can be processed on the logs
  65.  */
  66. list<list> mb_actions = [];
  67.  
  68.  
  69. // status variables
  70.  
  71. /**
  72.  * current lines of the selected log
  73.  */
  74. list<string> lines = [];
  75.  
  76.  
  77. // local functions
  78.  
  79. /**
  80.  * Get the map describing the particular log file from its index
  81.  * @param index integer index of the log file
  82.  * @return a map describing the log file
  83.  */
  84. define map<string,any> Index2Descr (integer index) {
  85.     return logs[index]:$[];
  86. }
  87.  
  88. /**
  89.  * Get maximum lines to display for a log
  90.  * @param log_descr a map describing the log
  91.  * @return integer maximum log lines to display
  92.  */
  93. define integer GetMaxLines (map<string,any> log_descr) {
  94.     return log_descr["max_lines"]:param["max_lines"]:max_lines_default;
  95. }
  96.  
  97. /**
  98.  * Starts the log reading command via background agent
  99.  * @param index integer the index of the log file
  100.  */
  101. define void InitLogReading (integer index) {
  102.     SCR::Execute (.background.kill);
  103.     map<string, any> log_descr = Index2Descr (index);
  104.     integer max_lines = GetMaxLines (log_descr);
  105.     string command = (string) (log_descr["command"]:nil);
  106.     if (command == nil || command == "")
  107.     {
  108.     string file = log_descr["file"]:"";
  109.     if (file == nil || file == "")
  110.     {
  111.         // error report
  112.         Report::Error (_("Error occurred while reading the log."));
  113.         return;
  114.     }
  115.     string grep = log_descr["grep"]:"";
  116.     if (grep != "" && grep != nil)
  117.         grep = sformat ("| grep --line-buffered '%1'", grep);
  118.     string lc_command
  119.         = sformat ("cat %1 %2 | wc -l", log_descr["file"]:"", grep);
  120.     map bash_output = (map)SCR::Execute (.target.bash_output, lc_command);
  121.     command = "tail -f -n +0 " + log_descr["file"]:"";
  122.     string addon = "";
  123.     if (bash_output["exit"]:1 == 0)
  124.     {
  125.         string lc = bash_output["stdout"]:"";
  126.         lc = filterchars (lc, "1234567890");
  127.         integer lines_count = tointeger (lc);
  128.         lines_count = lines_count - 2 * max_lines;
  129.         // don't know why without
  130.         // doubling it discards more lines, out of YaST2
  131.         // it works
  132.         if (max_lines != 0 && lines_count > 0)
  133.         addon = sformat ("| tail -n +%1", lines_count);
  134.     }
  135.     command = sformat ("%1 %2 %3", command, grep, addon);
  136.     }
  137.     y2milestone ("Calling background agent with command %1", command);
  138.     boolean cmdret = (boolean)SCR::Execute (.background.run_output, command);
  139.     if (! cmdret)
  140.     {
  141.     // error report
  142.     Report::Error (_("Error occurred while reading the log."));
  143.     return;
  144.     }
  145. }
  146.  
  147. /**
  148.  * Kill processes running on the backgrouns
  149.  * @param key log widget key
  150.  */
  151. define void KillBackgroundProcess (string key) {
  152.     SCR::Execute (.background.kill);
  153. }
  154.  
  155. /**
  156.  * Remove unneeded items from a list
  157.  * If max_lines is 0, then don't remove anything
  158.  * @param lines a list of strings representing log lines
  159.  * @param max_lines integer lines that should be saved
  160.  * @return a list last max_lines of lines
  161.  * FIXME probably used in multiple locations!!!
  162.  * FIXME variables are global, no need to have them as parameters
  163.  */
  164. define list<string> DeleteOldLines (list<string> lines, integer max_lines) {
  165.     if (0 == max_lines)
  166.     return lines;
  167.     integer sl = size (lines);
  168.     if (sl > max_lines)
  169.     {
  170.     lines = filter (string l, lines, {
  171.         sl = sl -1;
  172.         return sl < max_lines;
  173.     });
  174.     }
  175.     return lines;
  176. }
  177.  
  178. /**
  179.  * Fills the log widget with initial data got from the background agent
  180.  * @param index integer index of the log file
  181.  */
  182. define void FillWidgetWithData (integer index) {
  183.     sleep (100);
  184.     map<string,any> log = Index2Descr (index);
  185.     integer max_lines = GetMaxLines (log);
  186.  
  187.     integer count = (integer)SCR::Read (.background.newlines);
  188.  
  189.     if (count > 0)
  190.     {
  191.     lines = (list<string>) SCR::Read (.background.newout);
  192.     lines = DeleteOldLines (lines, max_lines);
  193.     UI::ChangeWidget (`id (`_cwm_log), `Value,
  194.         mergestring (lines, "\n") + "\n");
  195.     }
  196. }
  197.  
  198. /**
  199.  * Get the help for the log in case of multiple logs
  200.  * @return string part of the log
  201.  */
  202. global define string LogSelectionHelp () {
  203.     // help for the log widget, part 1, alt. 1
  204.     return _("<p><b><big>Displayed Log</big></b><br>
  205. Use <b>Log</b> to select the log to display. It will be displayed in
  206. the field below.</p>
  207. ");
  208. }
  209.  
  210. /**
  211.  * Get the help for the log in case of a single log
  212.  * @return string part of the log
  213.  */
  214. global define string SingleLogHelp () {
  215.     // help for the log widget, part 1, alt. 2
  216.     return _("<p><b><big>The Log</big></b><br>
  217. This screen displays the log.</p>");
  218.  
  219. }
  220.  
  221. /**
  222.  * Get the second part of the help for the log in case of advanced functions
  223.  *  and save support
  224.  * @param label tge label of the menu button
  225.  * @return string part of the log
  226.  */
  227. global define string AdvancedSaveHelp (string label) {
  228.     // help for the log widget, part 2, alt. 1, %1 is a menu button label
  229.     return sformat (_("<p>
  230. To process advanced actions or save the log into a file, click <b>%1</b>
  231. and select the action to process.</p>"), label);
  232.  
  233. }
  234.  
  235. /**
  236.  * Get the second part of the help for the log in case of advanced functions
  237.  * @param label tge label of the menu button
  238.  * @return string part of the log
  239.  */
  240. global define string AdvancedHelp (string label) {
  241.     // help for the log widget, part 2, alt. 2, %1 is a menu button label
  242.     return sformat (_("<p>
  243. To process advanced actions, click <b>%1</b>
  244. and select the action to process.</p>"), label);
  245.  
  246. }
  247.  
  248. /**
  249.  * Get the second part of the help for the log in case of save support
  250.  * @return string part of the log
  251.  */
  252. global define string SaveHelp () {
  253.     // help for the log widget, part 2, alt. 3
  254.     return _("<p>
  255. To save the log into a file, click <b>Save Log</b> and select the file
  256. to which to save the log.</p>
  257. ");
  258. }
  259.  
  260. /**
  261.  * Get the help of the widget
  262.  * @param logs integer count of displayed logs
  263.  * @param parameters map parameters of the log to display
  264.  * @return string help to the widget
  265.  */
  266. define string CreateHelp (integer logs,
  267.     map parameters)
  268. {
  269.     string help = parameters["help"]:"";
  270.     if (help != "" && help != nil)
  271.     return help;
  272.  
  273.     string adv_button = parameters["mb_label"]:"";
  274.     if (adv_button == "" || adv_button == nil)
  275.     {
  276.     // menu button
  277.     adv_button = _("Ad&vanced");
  278.     }
  279.  
  280.     if (regexpmatch (adv_button, "^.*&.*$"))
  281.     adv_button = regexpsub (adv_button, "^(.*)&(.*)$", "\\1\\2");
  282.  
  283.     boolean save = parameters["save"]:false;
  284.     if (save == nil)
  285.     save = false;
  286.  
  287.     list<map<string,any> > actions_lst = parameters["actions"]:[];
  288.     if (actions_lst == nil)
  289.     actions_lst = [];
  290.     integer actions = size (actions_lst);
  291.  
  292.     if (save)
  293.     actions = actions + 1;
  294.  
  295.     if (logs > 1)
  296.     {
  297.     help = LogSelectionHelp ();
  298.     }
  299.     else if (actions >= 1 || save)
  300.     {
  301.     help = SingleLogHelp ();
  302.     }
  303.     else
  304.     {
  305.     return "";
  306.     }
  307.  
  308.     if (actions >= 2)
  309.     {
  310.     if (save)
  311.     {
  312.         help = help + AdvancedSaveHelp (adv_button);
  313.  
  314.     }
  315.     else
  316.     {
  317.         help = help + AdvancedHelp (adv_button);
  318.     }
  319.  
  320.     }
  321.     else if (save)
  322.     {
  323.     help = help + SaveHelp ();
  324.     }
  325.  
  326.     return help;
  327. }
  328.  
  329. /**
  330.  * Get the combo box of the available log files
  331.  * @param log_maps a list of maps describing all the logs
  332.  * @return term the combo box widget
  333.  */
  334. define term GetLogSelectionCombo (list<map<string,any> > log_maps) {
  335.     term selection_combo = `Empty ();
  336.     if (size (log_maps) > 0)
  337.     {
  338.     integer index = -1;
  339.     list items = maplist (map m, log_maps, {
  340.         index = index + 1;
  341.         return `item (`id (index),
  342.         // combo box entry (only used as fallback in case
  343.         // of error in the YaST code)
  344.         m["log_label"]:m["command"]:m["file"]:_("Log"));
  345.     });
  346.     selection_combo = `ComboBox (`id (`cwm_log_files),
  347.         `opt (`notify, `hstretch),
  348.         _("&Log"),
  349.         items);
  350.     }
  351.     return selection_combo;
  352. }
  353.  
  354. /**
  355.  * Get the widget with the menu button with actions to be processed on the log
  356.  * @param actions a list of all actions
  357.  * @param save boolean true if the log should be offered to be saved
  358.  * @param mb_label label of the menu button, may be empty for default
  359.  * @return term widget with the menu button
  360.  */
  361. define term GetMenuButtonWidget (list<list> actions, boolean save,
  362.     string mb_label)
  363. {
  364.     list<any> menubutton = [];
  365.     if (save)
  366.     // menubutton entry
  367.     menubutton = add (menubutton, [`_cwm_log_save, _("&Save Log")]);
  368.  
  369.     if (size (actions) > 0)
  370.     {
  371.     integer index = 0;
  372.     foreach (list a, actions, {
  373.         menubutton = add (menubutton, [index, a[0]:""]);
  374.         index = index + 1;
  375.     });
  376.     }
  377.  
  378.     if (size (menubutton) > 1)
  379.     {
  380.     menubutton = filter (any m, menubutton, ``(
  381.         is(m,list) && (((list)m)[0]:nil != nil)
  382.     ));
  383.     menubutton = maplist (any m, menubutton, {
  384.         list ml = (list)m;
  385.         return `item (`id (ml[0]:nil), ml[1]:"");
  386.     });
  387.     if (mb_label == "" || mb_label == nil)
  388.     {
  389.         mb_label = _("Ad&vanced");
  390.     }
  391.     return `MenuButton (`id (`_cwm_log_menu), mb_label, menubutton);
  392.     }
  393.     else if (size (menubutton) == 1)
  394.     {
  395.     return `PushButton (`id (menubutton[0,0]:(any)""), menubutton[0,1]:"");
  396.     }
  397.     return `Empty ();
  398. }
  399.  
  400. /**
  401.  * Get the buttons below the box with the log
  402.  * @param popup boolean true if running in popup (and Close is needed)
  403.  * @param glob_param a map of global parameters of the log widget
  404.  * @param log_maps a list of maps describing all the logs
  405.  * @return term the widget with buttons
  406.  */
  407. define term GetButtonsBelowLog (boolean popup, map<string,any> glob_param,
  408.     list<map<string,any> > log_maps)
  409. {
  410.     term left = `Empty ();
  411.     term center = `Empty ();
  412.     term right = `Empty ();
  413.  
  414.     if (popup)
  415.     {
  416.     center = `PushButton (`id(`close), `opt (`key_F9),
  417.         Label::CloseButton ());
  418.  
  419.     if (haskey (glob_param, "help") && glob_param["help"]:"" != "")
  420.     {
  421.         left = `PushButton (`id (`help), Label::HelpButton ());
  422.     }
  423.     }
  424.  
  425.     boolean save = glob_param["save"]:false;
  426.     string mb_label = glob_param["mb_label"]:_("Ad&vanced");
  427.     list<list> actions = glob_param["actions"]:[];
  428.     right = GetMenuButtonWidget (actions, save, mb_label);
  429.  
  430.     return `HBox (
  431.     `HWeight (1, left),
  432.     `HStretch (),
  433.     `HWeight (1, center),
  434.     `HStretch (),
  435.     `HWeight (1, right)
  436.     );
  437. }
  438.  
  439. /**
  440.  * Get the default entry for the combo box with logs
  441.  * @param log_maps a list of maps describing all the logs
  442.  * @return integer the index of the default entry in the combo box
  443.  */
  444. define integer GetDefaultItemForLogsCombo (list<map<string,any> > log_maps) {
  445.     integer default_log = 0;
  446.     if (size (log_maps) > 0)
  447.     {
  448.     integer index = -1;
  449.     foreach (map m, log_maps, {
  450.         index = index + 1;
  451.         if (haskey (m, "default") && default_log == 0)
  452.         default_log = index;
  453.     });
  454.     }
  455.     return default_log;
  456. }
  457.  
  458. /**
  459.  * Switch the displayed log
  460.  * @param index integer index of the log to display
  461.  */
  462. define void LogSwitch (integer index) {
  463.     lines = [];
  464.     current_index = index;
  465.  
  466.     map<string, any> log_descr = Index2Descr (index);
  467.     // logview caption
  468.     string caption = log_descr["log_label"]:param["log_label"]:_("&Log");
  469.     integer max_lines = GetMaxLines (log_descr);
  470.     UI::ReplaceWidget (`_cwm_log_rp,
  471.     `LogView (`id (`_cwm_log), caption, 15, max_lines));
  472.  
  473.     InitLogReading (index);
  474.     FillWidgetWithData (index);
  475. }
  476.  
  477. /**
  478.  * Initialize the displayed log
  479.  * @param key log widget key
  480.  * @param key table widget key
  481.  */
  482. global define void LogInit (string key) {
  483.     param = CWM::GetProcessedWidget ();
  484.     current_index = param["_cwm_default_index"]:0;
  485.     mb_actions = param["_cwm_button_actions"]:[];
  486.     logs = param["_cwm_log_files"]:[];
  487.     if (UI::WidgetExists (`id (`cwm_log_files)))
  488.     UI::ChangeWidget (`id (`cwm_log_files), `value, current_index);
  489.     LogSwitch (current_index);
  490. }
  491.  
  492. /**
  493.  * Handle the event on the log view widget
  494.  * @param key log widget key
  495.  * @param event map event to handle
  496.  * @return symbol always nil
  497.  */
  498. global define symbol LogHandle (string key, map event) {
  499.     param = CWM::GetProcessedWidget ();
  500.     map<string,any> log = Index2Descr (current_index);
  501.     integer max_lines = GetMaxLines (log);
  502.     integer count = (integer)SCR::Read (.background.newlines);
  503.     if (count > 0)
  504.     {
  505.     list<string> new_lines = (list<string>)
  506.         SCR::Read (.background.newout);
  507.     foreach (string l, new_lines, {
  508.         UI::ChangeWidget (`id (`_cwm_log), `LastLine, l + "\n");
  509.     });
  510.     lines = (list<string>) merge (lines, new_lines);
  511.     lines = DeleteOldLines (lines, max_lines);
  512.     }
  513.     any ret = event["ID"]:nil;
  514.     // save the displayed log to file
  515.     if (ret == `_cwm_log_save)
  516.     {
  517.     string filename = UI::AskForSaveFileName(
  518.         // popup caption
  519.         "/tmp", "*.log", _("Save Log as..."));
  520.     if (filename != nil)
  521.     {
  522.         SCR::Write (.target.string, filename,
  523.         mergestring (lines, "\n") + "\n");
  524.     }
  525.     }
  526.     // other operation specified by user
  527.     else if (ret != nil && is (ret, integer))
  528.     {
  529.     integer iret = (integer)ret;
  530.     void() func = (void())(mb_actions[iret, 1]:nil);
  531.     if (func != nil)
  532.         func ();
  533.     if (mb_actions[iret, 2]:nil == true)
  534.     {
  535.         SCR::Execute (.background.kill);
  536.         UI::ChangeWidget (`id (`_cwm_log), `Value, "");
  537.         InitLogReading (current_index);
  538.     }
  539.     }
  540.     // switch displayed log file
  541.     else if (ret == `cwm_log_files)
  542.     {
  543.     integer index = (integer)
  544.         UI::QueryWidget (`id (`cwm_log_files), `Value);
  545.     LogSwitch (index);
  546.     }
  547.     return nil;
  548. }
  549.  
  550. /**
  551.  * Get the map with the log view widget
  552.  * @param parameters map parameters of the widget to be created, will be
  553.  *  unioned with the generated map
  554.  * <pre>
  555.  *  - "save" -- boolean, if true, then log saving is possible
  556.  *  - "actions" -- list, allows to specify additional actions.
  557.  *                 Each member is a 2- or 3-entry list, first entry is a
  558.  *                 label for the menubutton, the second one is a function
  559.  *                 that will be called when the entry is selected,
  560.  *                 the signature of the function must be void(),
  561.  *            optional 3rd argument, if set to true, forces
  562.  *            restarting of the log displaying command after the
  563.  *            action is performed
  564.  *  - "mb_label" -- string, label of the menubutton, if not specified,
  565.  *                  then "Advanced" is used
  566.  *  - "max_lines" -- integer, maximum of lines to be displayed. If 0,
  567.  *                   then display whole file. Default is 100.
  568.  *  - "help" -- string for a rich text, help to be offered via a popup
  569.  *              when user clicks the "Help" button. If not present,
  570.  *              default help is shown or Help button is hidden.
  571.  * - "widget_height" -- height of the LogView widget, to be adjusted
  572.  *                      so that the widget fits into the dialog well.
  573.  *                      Test it to find the best value, 15 seems to be
  574.  *                      good value (is default if not specified)
  575.  * </pre>
  576.  * @param log_files a list of logs that will be displayed
  577.  * <pre>
  578.  *  - "file" -- string, filename with the log
  579.  *  - "grep" -- string, basic regular expression to be grepped
  580.  *              in the log (for getting relevant  parts of
  581.  *              /var/log/messages. If empty or not present, whole file
  582.  *              is used
  583.  *  - "command" -- allows to specify comand to get the log for cases
  584.  *                 where grep isn't enough. If used, file and grep entries
  585.  *                 are ignored
  586.  *  - "log_label" -- header of the LogView widget, if not set, then the file
  587.  *                   name or the command is used
  588.  *  - "default" -- define and set to true to make this log be active after
  589.  *                 widget is displayed. If not defiend for any log, the
  590.  *                 first log is automatically default. If defined for multiple
  591.  *                 logs, the first one is active
  592.  * </pre>
  593.  * @return map the log widget
  594.  */
  595. global define map CreateWidget (map<string,any> parameters,
  596.     list<map<string,any> > log_files)
  597. {
  598.     // logview caption
  599.     string caption = param["log_label"]:_("&Log");
  600.     integer max_lines = param["max_lines"]:max_lines_default;
  601.     integer height = param["widget_height"]:15;
  602.  
  603.     integer default_index = GetDefaultItemForLogsCombo (log_files);
  604.     term top_bar = GetLogSelectionCombo (log_files);
  605.     term bottom_bar = GetButtonsBelowLog (false, parameters, log_files);
  606.  
  607.     return union (
  608.     $[
  609.         "widget" : `custom,
  610.         "custom_widget" : `VBox (
  611.         top_bar,
  612.         `ReplacePoint (`id (`_cwm_log_rp),
  613.             `LogView (`id (`_cwm_log), caption, height, max_lines)
  614.         ),
  615.         bottom_bar
  616.         ),
  617.         "init" : LogInit,
  618.         "handle" : LogHandle,
  619.         "cleanup" : KillBackgroundProcess,
  620.         "ui_timeout" : 1000,
  621.         "_cwm_default_index" : default_index,
  622.         "_cwm_log_files" : log_files,
  623.         "_cwm_button_actions" : [],
  624.         "help" : CreateHelp (size (log_files), parameters),
  625.     ],
  626.     parameters
  627.     );
  628. }
  629.  
  630.  
  631. // old functions for displaying log as a popup
  632.  
  633.  
  634. /**
  635.  * Main function for displaying logs
  636.  * @param parameters map description of parameters, with following keys
  637.  * <pre>
  638.  *  - "file" -- string, filename with the log
  639.  *  - "grep" -- string, basic regular expression to be grepped
  640.  *              in the log (for getting relevant  parts of
  641.  *              /var/log/messages. If empty or not present, whole file
  642.  *              is used
  643.  *  - "command" -- allows to specify comand to get the log for cases
  644.  *                 where grep isn't enough. If used, file and grep entries
  645.  *                 are ignored
  646.  *  - "save" -- boolean, if true, then log saving is possible
  647.  *  - "actions" -- list, allows to specify additional actions.
  648.  *                 Each member is a 2- or 3-entry list, first entry is a
  649.  *                 label for the menubutton, the second one is a function
  650.  *                 that will be called when the entry is selected,
  651.  *                 the signature of the function must be void(),
  652.  *            optional 3rd argument, if set to true, forces
  653.  *            restarting of the log displaying command after the
  654.  *            action is performed
  655.  *  - "help" -- string for a rich text, help to be offered via a popup
  656.  *              when user clicks the "Help" button. If not present,
  657.  *              Help button isn't shown
  658.  *  - "mb_label" -- string, label of the menubutton, if not specified,
  659.  *                  then "Advanced" is used
  660.  *  - "max_lines" -- integer, maximum of lines to be displayed. If 0,
  661.  *                   then display whole file. Default is 100.
  662.  *  - "log_label" -- header of the LogView widget, if not set, then "Log"
  663.  *                   is used
  664.  * </pre>
  665.  */
  666. global define void Display (map<string, any> parameters) {
  667.     param = parameters;
  668.  
  669.     // menubutton
  670.     string mb_label = param["mb_label"]:_("Ad&vanced");
  671.     integer max_lines = param["max_lines"]:max_lines_default;
  672.     string log_label = param["log_label"]:_("&Log");
  673.  
  674.     logs = [param];
  675.  
  676.     InitLogReading (0);
  677.  
  678.     term button_line = GetButtonsBelowLog (true, param, [param]);
  679.  
  680.         UI::OpenDialog (`HBox (`HSpacing (1), `VBox (
  681.             `VSpacing (1),
  682.             `HSpacing (70),
  683.             // log view header
  684.             `LogView (`id (`_cwm_log), log_label, 19, max_lines),
  685.             `VSpacing (1),
  686.         button_line,
  687.             `VSpacing (1)), `HSpacing (1)));
  688.  
  689.     if (param["help"]:"" != "")
  690.     {
  691.         UI::ReplaceWidget (`id (`rep_left),
  692.         `PushButton (`id (`help), Label::HelpButton ()));
  693.     }
  694.     mb_actions = param["actions"]:[];
  695.  
  696.     FillWidgetWithData (0);
  697.  
  698.         any ret = nil;
  699.         while (ret != `close && ret != `cancel)
  700.     {
  701.         map event = (map)UI::WaitForEvent (1000);
  702.         ret = event["ID"]:nil;
  703.         if (ret == `help)
  704.         {
  705.         UI::OpenDialog (`VBox (
  706.             `RichText (`id (`help_text), param["help"]:""),
  707.             `HBox (
  708.             `HStretch (),
  709.             `PushButton (`id (`close), Label::CloseButton ()),
  710.             `HStretch ()
  711.             )
  712.         ));
  713.         while (ret != `close && ret != `cancel)
  714.             ret = UI::UserInput ();
  715.         ret = nil;
  716.         UI::CloseDialog ();
  717.         }
  718.         else
  719.         {
  720.         LogHandle ("", event);
  721.         }
  722.     }
  723.     SCR::Execute (.background.kill);
  724.         UI::CloseDialog ();
  725.         return;
  726. }
  727.  
  728. /**
  729.  * Display specified file, list 100 lines
  730.  * @param file string filename of file with the log
  731.  */
  732. global define void DisplaySimple (string file) {
  733.     Display ($[ "file" : file ]);
  734. }
  735.  
  736. /**
  737.  * Display log with filtering with 100 lines
  738.  * @param file string filename of file with the log
  739.  * @param grep string basic regular expression to be grepped in file
  740.  */
  741. global define void DisplayFiltered (string file, string grep) {
  742.     Display ($[ "file" : file, "grep" : grep ]);
  743. }
  744.  
  745.  
  746. }
  747.