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 / clients / inst_proposal.ycp < prev    next >
Text File  |  2006-11-29  |  34KB  |  1,235 lines

  1. /**
  2.  * File:    clients/inst_proposal.ycp
  3.  * Module:    Installation
  4.  * Summary:    Create and display proposal
  5.  * Authors:    Stefan Hundhammer <sh@suse.de>
  6.  *        Arvin Schnell <arvin@suse.de>
  7.  *        Jiri Srain <jsrain@suse.cz>
  8.  *        Lukas Ocilka <locilka@suse.cz>
  9.  *
  10.  * $Id: inst_proposal.ycp 34452 2006-11-20 08:00:07Z locilka $
  11.  *
  12.  * Create and display reasonable proposal for basic
  13.  * installation and call sub-workflows as required
  14.  * on user request.
  15.  *
  16.  * See also file proposal-API.txt for details.
  17.  */
  18.  
  19. {
  20.     textdomain "installation";
  21.  
  22.     import "Label";
  23.     import "Mode";
  24.     import "Stage";
  25.     import "AutoinstConfig";
  26.     import "Wizard";
  27.     import "HTML";
  28.     import "Popup";
  29.     import "Language";
  30.     import "GetInstArgs";
  31.  
  32.     include "installation/misc.ycp";
  33.  
  34.     define void retranslate_proposal_dialog();
  35.     define symbol load_matching_submodules_list();
  36.     define boolean get_submod_descriptions_and_build_menu();
  37.     define void make_proposal( boolean force_reset, boolean language_changed );
  38.     define string format_sub_proposal( map prop );
  39.     define void build_dialog ();
  40.     define void set_icon();
  41.     define string help_text();
  42.  
  43.     // values used in defined functions
  44.  
  45.     map         proposal_properties = $[];
  46.     list<string>    submodules = [];
  47.     list<string>    submodules_presentation = [];
  48.     map<string,integer> mod2tab = $[]; // module -> tab it is in
  49.     list<string>    display_only_modules = []; // modules which do not have propose, but only summary
  50.     integer        current_tab = 0; // ID of current tab
  51.     boolean        has_tab = false; // true if is tabbed proposal
  52.     map<string, string> html = $[]; // proposals of all modules - HTML part
  53.     list<string>    present_only = [];
  54.     list<string>    locked_modules = [];
  55.     list    titles     = [];
  56.     map        submod2id  = $[];
  57.     map        id2submod  = $[];
  58.     map        link2submod = $[];
  59.     boolean    have_blocker = false;
  60.     string    proposal_mode = "";
  61.  
  62.     symbol proposal_result = nil;
  63.  
  64.     // skip if not interactive mode.
  65.     if (!AutoinstConfig::Confirm && Mode::autoinst ()) {
  66.     return `auto;
  67.     }
  68.  
  69.     /**
  70.      * Display preformatted proposal in the RichText widget
  71.      *
  72.      * @param proposal human readable proposal preformatted in HTML
  73.      **/
  74.  
  75.     define void display_proposal( string proposal ) ``{
  76.     UI::ChangeWidget(`id(`proposal), `Value, proposal );
  77.     };
  78.  
  79.  
  80.     /**
  81.      * Call a submodule's MakeProposal() function.
  82.      *
  83.      * @param  submodule    name of the submodule's proposal dispatcher
  84.      * @param  force_reset    discard any existing (cached) proposal
  85.      * @param  language_changed    installation language changed since last call
  86.      * @return proposal_map    see proposal-API.txt
  87.      **/
  88.  
  89.     define map submod_make_proposal( string submodule, boolean force_reset,
  90.                      boolean language_changed )
  91.     ``{
  92.     map proposal = (map) WFM::CallFunction ( submodule, [ "MakeProposal",
  93.                             $[ "force_reset" : force_reset,
  94.                                "language_changed" : language_changed ] ] );
  95.     y2debug( "%1 MakeProposal() returns %2", submodule, proposal );
  96.  
  97.     return proposal;
  98.     };
  99.  
  100.  
  101.     /**
  102.      * Call a submodule's AskUser() function.
  103.      *
  104.      * @param  submodule    name of the submodule's proposal dispatcher
  105.      * @param  has_next        force a "next" button even if the submodule would otherwise rename it
  106.      * @return workflow_sequence see proposal-API.txt
  107.      **/
  108.  
  109.     define symbol submod_ask_user( string submodule, map<string,any> additional_info )
  110.     ``{
  111.     // Call the AskUser() function
  112.  
  113.     map     ask_user_result   = (map) WFM::CallFunction( submodule,
  114.         [ "AskUser", additional_info ] );
  115.     symbol  workflow_sequence = ask_user_result["workflow_sequence"]:`next;
  116.     boolean language_changed  = ask_user_result["language_changed"]:false;
  117.     boolean mode_changed      = ask_user_result["mode_changed"]:false;
  118.     boolean rootpart_changed  = ask_user_result["rootpart_changed"]:false;
  119.  
  120.     if (workflow_sequence != `cancel && workflow_sequence != `back &&
  121.         workflow_sequence != `abort && workflow_sequence != `finish)
  122.     {
  123.         if ( language_changed )
  124.         {
  125.         retranslate_proposal_dialog();
  126.         Pkg::SetLocale (Language::language);
  127.         Pkg::SetAdditionalLocales ([Language::language]);
  128.         }
  129.  
  130.         /*
  131.         if (mode_changed || rootpart_changed ||
  132.         workflow_sequence == `finish)
  133.         {
  134.         RootPart::UnmountPartitions (false);
  135.         }
  136.         */
  137.  
  138.         if (mode_changed)
  139.         {
  140.         Wizard::SetHelpText (help_text ());
  141.  
  142.         build_dialog();
  143.         load_matching_submodules_list();
  144.         if (!get_submod_descriptions_and_build_menu ())
  145.         {
  146.             y2error ("i'm in dutch");
  147.         }
  148.         }
  149.  
  150.         // Make a new proposal based on those user changes
  151.         make_proposal( false, language_changed );
  152.     }
  153.  
  154.     return workflow_sequence;
  155.     };
  156.  
  157.  
  158.     /**
  159.      * Call a submodule's Description() function.
  160.      *
  161.      * @param  submodule    name of the submodule's proposal dispatcher or nil if no such module
  162.      * @return description_map    see proposal-API.txt
  163.      **/
  164.  
  165.     define map submod_description (string submodule)
  166.     ``{
  167.     map description = (map) WFM::CallFunction( submodule, [ "Description", $[] ] );
  168.  
  169.     return description;
  170.     };
  171.  
  172.  
  173.     /**
  174.      * Call each submodule's MakeProposal() function in turn and display the
  175.      * proposals in the RichText widget.
  176.      *
  177.      * @param  force_reset    discard any existing (cached) proposal
  178.      * @param  language_changed    installation language changed since last call
  179.      **/
  180.  
  181.     define void make_proposal( boolean force_reset, boolean language_changed )
  182.     ``{
  183.     integer tab_to_switch = 999;
  184.     boolean current_tab_affected = false;
  185.     integer no = 0;
  186.     map prop_map = $[];
  187.     boolean skip_the_rest = false;
  188.     have_blocker = false;
  189.  
  190.     link2submod = $[];
  191.  
  192.     html = $[];
  193.     foreach ( string submod, submodules, ``{
  194.         string prop = "";
  195.  
  196.             if (!contains(locked_modules, submod))
  197.             {
  198.         string heading = issubstring (
  199.             titles[no]:"",
  200.             "<a")
  201.             ? titles[no]:_("ERROR: Missing Title")
  202.             : HTML::Link( titles[no]:_("ERROR: Missing Title"), submod2id[submod]:"");
  203.  
  204.         // heading in proposal, in case the module doesn't create one
  205.         prop = prop + HTML::Heading( heading);
  206.         } else {
  207.         prop = prop + HTML::Heading(
  208.            // heading in proposal, in case the module doesn't create one
  209.                     titles[no]:_("ERROR: Missing Title"));
  210.             }
  211.  
  212.         html[submod] = prop + HTML::Para( _("Analyzing your system..."));
  213.         no = no + 1;
  214.     });
  215.  
  216.     no = 0;
  217.  
  218.     Wizard::DisableNextButton ();
  219.     UI::BusyCursor();
  220.  
  221.         y2debug("Submodules list before execution: %1", submodules );
  222.     foreach ( string submod, submodules,
  223.     ``{
  224.         string prop = "";
  225.         if ( ! skip_the_rest )
  226.         {
  227.             if (!contains(locked_modules, submod))
  228.             {
  229.             string heading = issubstring (
  230.                 titles[no]:"",
  231.                 "<a")
  232.             ? titles[no]:_("ERROR: Missing Title")
  233.             : HTML::Link( titles[no]:_("ERROR: Missing Title"), submod2id[submod]:"");
  234.  
  235.             // heading in proposal, in case the module doesn't create one
  236.             prop = prop + HTML::Heading( heading);
  237.             } else {
  238.             prop = prop + HTML::Heading(
  239.                     titles[no]:_("ERROR: Missing Title"));
  240.             }
  241.  
  242.         prop_map = submod_make_proposal( submod, force_reset, language_changed );
  243.  
  244.         // check if it is needed to switch to another tab
  245.         // because of an error
  246.         if (haskey (mod2tab, submod))
  247.         {
  248.             y2milestone("Mod2Tab: '%1'", mod2tab[submod]:nil);
  249.             symbol warn_level = prop_map["warning_level"]:`ok;
  250.             if (warn_level == nil)
  251.             warn_level = `ok;
  252.             if (contains ([`blocker, `fatal, `error], warn_level))
  253.             {
  254.             if (mod2tab[submod]:999 < tab_to_switch)
  255.             {
  256.                 tab_to_switch = mod2tab[submod]:999;
  257.             }
  258.             if (mod2tab[submod]:999 == current_tab)
  259.             {
  260.                 current_tab_affected = true;
  261.             }
  262.             }
  263.         }
  264.  
  265.         // update link map
  266.         if (haskey (prop_map, "links"))
  267.         {
  268.             foreach ( string link, prop_map["links"]:[], ``{
  269.             link2submod[link] = submod;
  270.             });
  271.         }
  272.         }
  273.  
  274.         if ( prop_map["language_changed"]:false
  275.          && ! skip_the_rest)
  276.         {
  277.         skip_the_rest = true;
  278.         retranslate_proposal_dialog();
  279.         make_proposal( force_reset, true );
  280.         }
  281.  
  282.         if ( ! skip_the_rest )
  283.         {
  284.         prop = prop + format_sub_proposal( prop_map );
  285.  
  286.         html[submod] = prop;
  287.  
  288.         // now do the complete html
  289.         string proposal = "";
  290.         foreach (string mod, submodules_presentation, ``{
  291.             proposal = proposal + html[mod]:"";
  292.         });
  293.         display_proposal( proposal );
  294.  
  295.         // display_proposal( prop );
  296.         no = no + 1;
  297.         }
  298.  
  299.         if ( prop_map["warning_level"]:`none == `fatal )
  300.         {
  301.         skip_the_rest = true;
  302.         }
  303.     });
  304.     if (has_tab && tab_to_switch < 999 && ! current_tab_affected)
  305.     {
  306.         // FIXME copy-paste from event loop (but for last 2 lines)
  307.         current_tab = tab_to_switch;
  308.         load_matching_submodules_list();
  309.         string proposal = "";
  310.         foreach (string mod, submodules_presentation, ``{
  311.         proposal = proposal + html[mod]:"";
  312.         });
  313.         display_proposal( proposal );
  314.         get_submod_descriptions_and_build_menu ();
  315.         y2milestone("Switching to tab '%1'", current_tab);
  316.         if (UI::HasSpecialWidget (`DumbTab))
  317.         UI::ChangeWidget (`id (`_cwm_tab), `CurrentItem, current_tab);
  318.     }
  319.  
  320.     // now do the display-only proposals
  321.  
  322.     Wizard::EnableNextButton ();
  323.     UI::NormalCursor();
  324.     };
  325.  
  326.  
  327.     /**
  328.      * Format a submodule's proposal in HTML
  329.      *
  330.      * @param  prop proposal map - see proposal-API.txt
  331.      * @return HTML string
  332.      **/
  333.  
  334.     define string format_sub_proposal( map prop )
  335.     ``{
  336.     string html = "";
  337.     string warning = prop["warning"]:"";
  338.  
  339.     if ( warning != nil && warning != "" )
  340.     {
  341.         symbol level = prop["warning_level"]:`warning;
  342.  
  343.         if      ( level == `notice  )    warning = HTML::Bold( warning );
  344.         else if ( level == `warning )    warning = HTML::Colorize( warning, "red" );
  345.         else if ( level == `error    )    warning = HTML::Colorize( warning, "red" );
  346.         else if ( level == `blocker    || level == `fatal )
  347.         {
  348.         have_blocker = true;
  349.         warning = HTML::Colorize( warning, "red" );
  350.         }
  351.  
  352.         html = html + HTML::Para( warning );
  353.     }
  354.  
  355.     string preformatted_prop = prop["preformatted_proposal"]:"";
  356.     if (preformatted_prop == nil)
  357.         preformatted_prop = "";
  358.  
  359.     if ( preformatted_prop != "" )
  360.     {
  361.         html = html + preformatted_prop;
  362.     }
  363.     else
  364.     {
  365.         // fallback proposal, means usually an internal error
  366.         list<string> raw_prop = prop["raw_proposal"]:[_("ERROR: No proposal")];
  367.         html = html + HTML::List( raw_prop );
  368.     }
  369.  
  370.     return html;
  371.     };
  372.  
  373.  
  374.  
  375.  
  376.     /**
  377.      * Call a submodule's Write() function.
  378.      *
  379.      * @param  submodule    name of the submodule's proposal dispatcher
  380.      * @return success        true if Write() was successful of if there is no Write() function
  381.      **/
  382.     define boolean submod_write_settings( string submodule )
  383.     ``{
  384.     map result = (map) WFM::CallFunction( submodule, [ "Write", $[] ] );
  385.     if ( result == nil )
  386.         result = $[];
  387.  
  388.     return result["success"]:true;
  389.     };
  390.  
  391.     define void PreInstallFunctions () {
  392.     y2milestone("PreInstallFunctions, Installation: %1, Stage: %2",
  393.         Mode::installation(), Stage::stage());
  394.     if (Mode::installation () && Stage::stage () == "initial") {
  395.         // FATE #300421: Import ssh keys from previous installations
  396.         // FATE #120103: Import Users From Existing Partition
  397.         y2milestone("PreInstallFunctions -- start --");
  398.         WFM::CallFunction ("inst_pre_install", []);
  399.         y2milestone("PreInstallFunctions -- end --");
  400.     }
  401.     }
  402.  
  403.     /**
  404.      * Call each submodule's "Write()" function to let it write its settings,
  405.      * i.e. the settings effective.
  406.      **/
  407.     define void write_settings() ``{
  408.  
  409.     boolean success = true;
  410.  
  411.     foreach (string submod, submodules, ``{
  412.         boolean submod_success = submod_write_settings( submod );
  413.         if (submod_success == nil)
  414.         submod_success = true;
  415.  
  416.         if ( ! submod_success )
  417.         y2error( "Write() failed for submodule %1", submod );
  418.  
  419.         success = success && submod_success;
  420.     });
  421.  
  422.     if ( ! success )
  423.     {
  424.         y2error( "Write() failed for one or more submodules" );
  425.         // Submodules handle their own error reporting
  426.  
  427.         // text for a message box
  428.         Popup::TimedMessage( _("Configuration saved.\nThere were errors."), 3 );
  429.     }
  430.     // else
  431.     // {
  432.     //     // text for a message box
  433.     //     Popup::TimedMessage( _("Configuration saved successfully."), 3 );
  434.     // }
  435.     };
  436.  
  437.  
  438.     /**
  439.      * Force a RichText widget to use the busy cursor
  440.      *
  441.      * @param widget_id  ID  of the widget, e.g. `id(`proposal)
  442.      **/
  443.     define void richtext_busy_cursor( any widget_id ) ``{
  444.         if (is(widget_id, symbol)) {
  445.             UI::ChangeWidget( (symbol) widget_id, `Enabled, false );
  446.         } else {
  447.             UI::ChangeWidget( (term) widget_id, `Enabled, false );
  448.         }
  449.     };
  450.  
  451.  
  452.     /**
  453.      * Switch a RichText widget back to use the normal cursor
  454.      *
  455.      * @param widget_id  ID  of the widget, e.g. `id(`proposal)
  456.      **/
  457.     define void richtext_normal_cursor( any widget_id ) ``{
  458.         if (is(widget_id, symbol)) {
  459.         UI::ChangeWidget( (symbol) widget_id, `Enabled, true );
  460.         } else {
  461.         UI::ChangeWidget( (term) widget_id, `Enabled, true );
  462.         }
  463.     };
  464.  
  465.  
  466.     /**
  467.      * Retranslate the proposal (wizard) dialog after the language is changed.
  468.      **/
  469.     define void retranslate_proposal_dialog() ``{
  470.  
  471.     y2debug( "Retranslating proposal dialog" );
  472.  
  473.     build_dialog();
  474.     ProductControl::RetranslateWizardSteps();
  475.     Wizard::RetranslateButtons();
  476.     get_submod_descriptions_and_build_menu();
  477.     };
  478.  
  479.  
  480.     /**
  481.      * Load a list of submodules. Try loading it from a file named 'file_name'
  482.      * from one of several predefined directories. If there is no such list,
  483.      * use 'fallback_list'.
  484.      *
  485.      * @param  file_name    YCP file name to load from
  486.      * @param  fallback_list    fallback list of submodule names (strings)
  487.      * @return submodules_list    list of submodule names (strings)
  488.      **/
  489.  
  490.     /* OBSOLETE CODE
  491.     define list load_submodules_list( string file_name, list fallback_list ) ``{
  492.  
  493.     list submodules = (list) SCR::Read(.target.ycp, [ file_name, [] ] );
  494.  
  495.     if ( submodules == [] )
  496.     {
  497.         submodules = (list) SCR::Read(.target.yast2, [ file_name, [] ] );
  498.     }
  499.  
  500.     if ( submodules == [] )
  501.     {
  502.         submodules = fallback_list;
  503.     }
  504.  
  505.     return submodules;
  506.     };
  507.  
  508.     */
  509.  
  510.  
  511.     /**
  512.      * Load a list of submodules matching the current internal states
  513.      *
  514.      * @return submodules_list    list of submodule names (strings)
  515.      **/
  516.     define symbol load_matching_submodules_list() ``{
  517.  
  518.     list< list > modules = [];
  519.  
  520.         modules = ProductControl::getProposals (
  521.                 Stage::stage (),
  522.                 Mode::mode (),
  523.                 proposal_mode );
  524.         if (modules == nil ) {
  525.             y2error("Error loading proposals");
  526.             return `abort;
  527.         }
  528.     
  529.         locked_modules = ProductControl::getLockedProposals (Stage::stage (), Mode::mode (), proposal_mode);
  530.     
  531.         y2milestone("getting proposals for stage: \"%1\" mode: \"%2\" proposal type: \"%3\"",
  532.                 Stage::stage (),
  533.                 Mode::mode (),
  534.                 proposal_mode );
  535.  
  536.     proposal_properties = ProductControl::getProposalProperties(Stage::stage (), Mode::mode (), proposal_mode);
  537.  
  538.         if (size(modules) == 0 )
  539.         {
  540.             y2error("No proposals available");
  541.             return `abort;
  542.         }
  543.  
  544.     // in normal mode we don't want to switch between installation and update
  545.     if (Mode::normal ())
  546.     {
  547.         modules = filter (list v, modules, ``(v[0]:"" != "mode_proposal"));
  548.     }
  549.  
  550.     // now create the list of modules and order of modules for presentation
  551.     submodules = maplist (list mod, modules, ``(mod[0]:"") );
  552.         y2milestone ("Execution order: %1", submodules);
  553.  
  554.     if (has_tab)
  555.     {
  556.         y2milestone ("Proposal uses tabs");
  557.         map data = ProductControl::getProposalProperties (
  558.                 Stage::stage (),
  559.                 Mode::mode (),
  560.                 proposal_mode );
  561.         submodules_presentation = data["proposal_tabs", current_tab, "proposal_modules"]:[];
  562.             // All proposal file names end with _proposal
  563.         submodules_presentation = maplist (string m, submodules_presentation, {
  564.         if (!issubstring(m, "_proposal"))
  565.             m = m + "_proposal";
  566.         return m;
  567.         });
  568.         integer index = -1;
  569.         mod2tab = $[];
  570.         list<list<string> > tmp_all_submods = maplist (map tab, data["proposal_tabs"]:[], {
  571.         index = index + 1;
  572.         foreach (string m, tab["proposal_modules"]:[], {
  573.             if (!issubstring(m, "_proposal"))
  574.             m = m + "_proposal";
  575.             if (index < mod2tab[m]:999)
  576.             mod2tab[m] = index;
  577.         });
  578.         return tab["proposal_modules"]:[];
  579.         });
  580.  
  581.         list<string> all_submods = flatten (tmp_all_submods);
  582.         all_submods = maplist (string m, all_submods, {
  583.         if (!issubstring(m, "_proposal"))
  584.             m = m + "_proposal";
  585.         return m;
  586.         });
  587.         display_only_modules = filter (string m, all_submods, {
  588.         return ! contains (submodules, m);
  589.         });
  590.         submodules = (list<string>)merge (submodules, display_only_modules);
  591.     }
  592.     else
  593.     {
  594.         y2milestone ("Proposal doesn't use tabs");
  595.         // sort modules according to presentation ordering
  596.         modules = sort (list mod1, list mod2, modules, ``(
  597.         mod1[1]:50 < mod2[1]:50
  598.         ));
  599.  
  600.         // setup the list
  601.         submodules_presentation = maplist (list mod, modules, ``(mod[0]:"") );
  602.     }
  603.  
  604.     y2milestone ("Presentation order: %1", submodules_presentation);
  605.         y2milestone ("Execution order: %1", submodules);
  606.     };
  607.  
  608.  
  609.     /**
  610.      * Find out if the target machine has a network card.
  611.      * @return true if a network card is found, false otherwise
  612.      **/
  613.     define boolean have_network_card() ``{
  614.  
  615.     // Maybe obsolete
  616.  
  617.     if ( Mode::test () )
  618.         return true;
  619.  
  620.     return size( (list<map>) SCR::Read(.probe.netcard) ) > 0;
  621.     };
  622.  
  623.  
  624.     /**
  625.      * Create the proposal dialog
  626.      * (the inner part, excluding the wizard frame)
  627.      **/
  628.     define void build_dialog () {
  629.  
  630.     // headline for installation proposal
  631.     
  632.     string headline = proposal_properties["label"]:"";
  633.  
  634.     
  635.         y2milestone("headline: %1", headline );
  636.     
  637.         if ( headline == "")
  638.         {
  639.         // dialog headline
  640.             headline =  _("Installation Settings");
  641.         }
  642.         else
  643.         {
  644.             headline = dgettext( ProductControl::getProposalTextDomain() ,
  645.                  headline );
  646.         }
  647.  
  648.     // icon for installation proposal
  649.     string icon = "";
  650.  
  651.     /* radiobuttons */
  652.     term skip_buttons =
  653.         `RadioButtonGroup (
  654.                    `VBox (
  655.                       `VSpacing(1),
  656.                       `Left(`RadioButton(`id(`skip), `opt(`notify),
  657.                              // Check box: Skip all the configurations in this dialog -
  658.                              // do this later manually or not at all
  659.                              // Translators: About 40 characters max,
  660.                              // use newlines for longer translations.
  661.                             // radio button
  662.                              _("&Skip Configuration"), false)),
  663.                       `Left(`RadioButton(`id(`dontskip), `opt(`notify),
  664.                             // radio button
  665.                              _("&Use Following Configuration"), true)),
  666.                       `VSpacing(1)
  667.                       )
  668.                    );
  669.  
  670.     /* change menu */
  671.     term menu_box =
  672.         `HBox (
  673.            `HStretch (),
  674.            `ReplacePoint(`id(`rep_menu),
  675.                 // menu button
  676.                  `MenuButton(`id(`menu_dummy), _("&Change..."), [`item(`id(`dummy), "" ) ] )
  677.                  ),
  678.            `HStretch ()
  679.            );
  680.  
  681.     term vbox = nil;
  682.  
  683.         boolean enable_skip = true;
  684.         if (haskey(proposal_properties, "enable_skip"))
  685.         {
  686.             enable_skip = proposal_properties["enable_skip"]:"yes" == "yes";
  687.         } else {
  688.             if (proposal_mode == "initial" || proposal_mode == "uml")
  689.                 enable_skip = false;
  690.             else
  691.                 enable_skip = true;
  692.         }
  693.         term rt = `RichText( `id(`proposal),
  694.                     // Initial contents of proposal subwindow while proposals are calculated
  695.                     HTML::Newlines( 3 ) + HTML::Para( _("Analyzing your system...") )
  696.                     );
  697.     map data = ProductControl::getProposalProperties (
  698.                 Stage::stage (),
  699.                 Mode::mode (),
  700.                 proposal_mode );
  701.     if (haskey (data, "proposal_tabs"))
  702.     {
  703.         has_tab = true;
  704.         integer index = -1;
  705.         list<map> tabs = data["proposal_tabs"]:[];
  706.         list<integer> tab_ids = maplist (map tab, tabs, {
  707.         index = index + 1;
  708.         return index;
  709.         });
  710.         if (UI::HasSpecialWidget (`DumbTab))
  711.         {
  712.         list<term> panes = maplist (integer t, tab_ids, {
  713.             string label = tabs[t, "label"]:"Tab";
  714.             return `item (`id (t), label, t == 0);
  715.         });
  716.         rt = `DumbTab (`id (`_cwm_tab), panes, rt);
  717.         }
  718.         else
  719.         {
  720.         term tabbar = `HBox ();
  721.         foreach (integer t, tab_ids, {
  722.             string label = tabs[t, "label"]:"Tab";
  723.             tabbar = add (tabbar, `PushButton (`id (t), label));
  724.         });
  725.         rt = `VBox (`Left(tabbar), `Frame( "", rt));
  726.         }
  727.     }
  728.     else
  729.     {
  730.         has_tab = false;
  731.     }
  732.     if (!enable_skip)
  733.     {
  734.         vbox = `VBox(
  735.              // Help message between headline and installation proposal / settings summary.
  736.              // May contain newlines, but don't make it very much longer than the original.
  737.              `Left( `Label( _("Click any headline to make changes or use the \"Change...\" menu below.") ) ),
  738.              rt,
  739.              menu_box
  740.              );
  741.     }
  742.     else
  743.     {
  744.         vbox = `VBox(
  745.              skip_buttons,
  746.              `HBox (
  747.                 `HSpacing (4),
  748.                 rt
  749.              ),
  750.              menu_box
  751.              );
  752.     }
  753.  
  754.     Wizard::SetContents(headline, vbox, help_text(),
  755.                 GetInstArgs::enable_back(),    // have_back_button
  756.                 false            // have_next_button
  757.                 );
  758.     set_icon();
  759.     if (UI::HasSpecialWidget (`DumbTab))
  760.         UI::ChangeWidget (`id (`_cwm_tab), `CurrentItem, current_tab);
  761.     if ( Stage::stage () == "initial" )
  762.         // push button
  763.         Wizard::ShowReleaseNotesButton (_("&Show Release Notes"), `rel_notes);
  764.     }
  765.  
  766.  
  767.  
  768.     /**
  769.      * Query all submodules about their descriptions, build a "Change" menu
  770.      * from that, and cache the descriptions for further usage: They will
  771.      * become hyperlinks in the RichText widget, too. Return false if no
  772.      * submodule exists.
  773.      **/
  774.     define boolean get_submod_descriptions_and_build_menu() ``{
  775.  
  776.     list        menu_list    = [];
  777.     list<string>    new_submodules    = [];
  778.     integer    no            = 1;
  779.     titles = [];
  780.     map descriptions = $[];
  781.  
  782.     foreach(string submod, submodules, ``{
  783.         map description   = submod_description( submod );
  784.  
  785.         if ( description == nil )
  786.         {
  787.         y2milestone( "Submodule %1 not available (not installed?)", submod );
  788.         }
  789.         else
  790.         {
  791.         if ( description != $[] )
  792.         {
  793.             description["no"] = no;
  794.             descriptions[submod] = description;
  795.             new_submodules = add( new_submodules, submod );
  796.             string title      = description["rich_text_title"]:description["rich_text_raw_title"]:submod;
  797.             string id         = description["id"             ]:sformat( "module_%1", no );
  798.  
  799.             titles            = add( titles,  title );
  800.             submod2id[submod] = id;
  801.             id2submod[id]     = submod;
  802.  
  803.             no = no + 1;
  804.         }
  805.         }
  806.     });
  807.  
  808.     submodules = new_submodules;    // maybe some submodules are not installed
  809.         y2milestone ("Execution order after rewrite: %1", submodules);
  810.  
  811.     // now build the menu button
  812.     foreach (string submod, submodules_presentation, {
  813.         map descr = descriptions[submod]:$[];
  814.         if (descr != $[])
  815.         {
  816.         integer no = descr["no"]:0;
  817.         string id = descr["id"]:sformat( "module_%1", no);
  818.         if (haskey (descr, "menu_titles"))
  819.         {
  820.             foreach (map<string,string> i, descr["menu_titles"]:[], {
  821.             string id = i["id"]:"";
  822.             string title = i["title"]:"";
  823.             if (id != "" && title != "")
  824.             {
  825.                 menu_list = add (menu_list, `item (`id (id),
  826.                 title + "..."));
  827.             }
  828.             else
  829.             {
  830.                 y2error ("Invalid menu item: %1", i);
  831.             }
  832.             });
  833.         }
  834.         else
  835.         {
  836.             string menu_title = descr["menu_title"]:descr["rich_text_title"]:submod;
  837.             menu_list = add( menu_list,
  838.             `item(`id( id ), menu_title + "..." ));
  839.         }
  840.         }
  841.     });
  842.  
  843.     // menu button item
  844.     menu_list = add( menu_list, `item(`id(`reset_to_defaults), _("&Reset to defaults") ) );
  845.     // menu button
  846.     UI::ReplaceWidget(`id(`rep_menu), `MenuButton(`id(`menu), _("&Change..."), menu_list ) );
  847.  
  848.     return no > 1;
  849.     };
  850.  
  851.  
  852.     /**
  853.      * Set an appropriate wizard icon for the current proposal mode.
  854.      *
  855.      * .desktop files may or may not be available (e.g. in the inst-sys they are not),
  856.      * so use icon names directly rather than looking them up in the .desktop file
  857.      * with Wizard::SetDesktopIcon().
  858.      **/
  859.     define void set_icon()
  860.     {
  861.     string icon = "yast-software";
  862.  
  863.     if ( proposal_mode == "network"    )
  864.         icon = "yast-network";
  865.     else if ( proposal_mode == "hardware" )
  866.         icon = "yast-controller";
  867.     else if ( proposal_properties["icon"]:"" != "" )
  868.         icon =  proposal_properties["icon"]:"";
  869.  
  870.         
  871.      // else if ( proposal_mode == `uml        ) icon = "";
  872.     // else if ( proposal_mode == `dirinstall  ) icon = "";
  873.  
  874.     Wizard::SetTitleIcon( icon );
  875.     };
  876.  
  877.  
  878.     /**
  879.      * Help text for proposal dialog.
  880.      *
  881.      * @return string help text
  882.      **/
  883.     define string help_text() ``{
  884.  
  885.     string help_text_string = "";
  886.  
  887.         // General part of the help text for all types of proposals
  888.     string how_to_change = _("<p>
  889. Change the values by clicking on the respective headline
  890. or by using the <b>Change...</b> menu.
  891. </p>
  892. ");
  893.  
  894.     if ( proposal_mode == "initial" && Mode::installation () )
  895.     {
  896.         // Help text for installation proposal
  897.         // General part ("You can change values...") is added as the next paragraph.
  898.         help_text_string = _("<p>
  899. Use <b>Accept</b> to perform a new installation with the values displayed.
  900. </p>
  901. ") + how_to_change;
  902.  
  903. // kicking out, bug #203811
  904. // no such headline
  905. //        // Help text for installation proposal, continued
  906. //        help_text_string = help_text_string + _("<p>
  907. //To update an existing &product; system instead of doing a new install,
  908. //click the <b>Mode</b> headline or select <b>Mode</b> in the
  909. //<b>Change...</b> menu.
  910. //</p>
  911. //");
  912.         /**
  913.          * Deliberately omitting "boot installed system" here to avoid
  914.          * confusion: The user will be prompted for that if Linux
  915.          * partitions are found.
  916.          * - sh@suse.de 2002-02-26
  917.          **/
  918.  
  919.         // Help text for installation proposal, continued
  920.         help_text_string = help_text_string + _("<p>
  921. Your hard disk has not been modified in any way, so you can still safely abort.
  922. </p>
  923. ");
  924.     }
  925.     else if ( proposal_mode == "initial" && Mode::update () )
  926.     {
  927.         // Help text for update proposal
  928.         // General part ("You can change values...") is added as the next paragraph.
  929.         help_text_string = _("<p>
  930. Use <b>Accept</b> to perform an update with the values displayed.
  931. </p>
  932. ") + how_to_change;
  933.  
  934.         /**
  935.          * Deliberately omitting "boot installed system" here to avoid
  936.          * confusion: The user will be prompted for that if Linux
  937.          * partitions are found.
  938.          * - sh@suse.de 2002-02-26
  939.          **/
  940.  
  941.         // Help text for installation proposal, continued
  942.         help_text_string = help_text_string + _("<p>
  943. Your hard disk has not been modified in any way, so you can still safely abort.
  944. </p>
  945. ");
  946.     }
  947.     else if ( proposal_mode == "network" )
  948.     {
  949.         // Help text for network configuration proposal
  950.         // General part ("You can change values...") is added as the next paragraph.
  951.         help_text_string = _("<p>
  952. Put the network settings into effect by pressing <b>Next</b>.
  953. </p>
  954. ") + how_to_change;
  955.     }
  956.     else if ( proposal_mode == "service" )
  957.     {
  958.         // Help text for service configuration proposal
  959.         // General part ("You can change values...") is added as the next paragraph.
  960.         help_text_string = _("<p>
  961. Put the service settings into effect by pressing <b>Next</b>.
  962. </p>
  963. ") + how_to_change;
  964.     }
  965.     else if ( proposal_mode == "hardware" )
  966.     {
  967.         // Help text for hardware configuration proposal
  968.         // General part ("You can change values...") is added as the next paragraph.
  969.         help_text_string = _("<p>
  970. Put the hardware settings into effect by pressing <b>Next</b>.
  971. </p>
  972. ") + how_to_change;
  973.     }
  974.     else if (proposal_mode == "uml")
  975.     {
  976.         // Proposal in uml module
  977.         help_text_string = _("<P><B>UML Installation Proposal</B></P>")
  978.         // help text
  979.         + _("<P>UML (User Mode Linux) installation allows you to start independent
  980. Linux virtual machines in the host system.</P>");
  981.     }
  982.     else if (proposal_properties["help"]:"" != "")
  983.     {
  984.         // Proposal help from control file module
  985.         help_text_string = dgettext( ProductControl::getProposalTextDomain() ,
  986.                   proposal_properties["help"]:"") +
  987.                 how_to_change;
  988.     }    
  989.     else
  990.     {
  991.         // Generic help text for other proposals (not basic installation or
  992.         // hardhware configuration.
  993.         // General part ("You can change values...") is added as the next paragraph.
  994.         help_text_string = _("<p>
  995. To use the settings as displayed, press <b>Next</b>.
  996. </p>
  997. ") + how_to_change;
  998.     }
  999.  
  1000.         if (size(locked_modules) > 0 )
  1001.     {
  1002.         // help text
  1003.             help_text_string = help_text_string + _("<p>Some proposals might be
  1004. locked by the system administrator, so cannot be changed. To change
  1005. a proposal that is locked, ask your system administrator.</p>");
  1006.         }
  1007.  
  1008.     return help_text_string;
  1009.     };
  1010.  
  1011.  
  1012.  
  1013.     /*-----------------------------------------------------------------------*/
  1014.     /*                    main()                     */
  1015.     /*-----------------------------------------------------------------------*/
  1016.  
  1017.  
  1018.  
  1019.     //
  1020.     // Create dialog
  1021.     //
  1022.     // This is done as early as possible for instant feedback, even though the
  1023.     // menu is still empty. Fortunately enough, nobody will notice this since
  1024.     // we also disable it until everything in there is known. This is to be
  1025.     // done before even the submodule descriptions are known since they usually
  1026.     // are in separate YCP files that liberally import other YCP modules which
  1027.     // in turn takes considerable time for the module constructors.
  1028.     //
  1029.  
  1030.     y2milestone( "Installation step #2" );
  1031.     proposal_mode = GetInstArgs::proposal();
  1032.  
  1033.     if (contains (ProductControl::DisabledProposals, proposal_mode))
  1034.     return `auto;
  1035.  
  1036.     proposal_properties = ProductControl::getProposalProperties(Stage::stage (), Mode::mode (), proposal_mode);
  1037.     build_dialog();
  1038.  
  1039.     //
  1040.     // Get submodule descriptions
  1041.     //
  1042.     proposal_result = load_matching_submodules_list();
  1043.     if (proposal_result == `abort)
  1044.         return `abort;
  1045.  
  1046.     UI::ChangeWidget(`id(`menu_dummy), `Enabled, false );
  1047.     richtext_busy_cursor(`id(`proposal ) );
  1048.  
  1049.     // The "next" button is disabled via Wizard::SetContents() until everything is set up allright
  1050.     Wizard::EnableNextButton();
  1051.  
  1052.     if (!get_submod_descriptions_and_build_menu ())
  1053.     {
  1054.     return `auto;
  1055.     }
  1056.  
  1057.  
  1058.     //
  1059.     // Make the initial proposal
  1060.     //
  1061.     make_proposal( false, false );
  1062.  
  1063.     // Set keyboard focus to the [Accept] or [Next] button
  1064.     Wizard::SetFocusToNextButton();
  1065.     if (Stage::initial ())
  1066.     Wizard::SetNextButton (`next, Label::AcceptButton ());
  1067.  
  1068.     //
  1069.     // Input loop
  1070.     //
  1071.  
  1072.     any input = nil;
  1073.  
  1074.     while ( true )
  1075.     {
  1076.     richtext_normal_cursor(`id(`proposal ) );
  1077.     input = Wizard::UserInput();
  1078.     y2milestone("Proposal - UserInput: '%1'", input);
  1079.     richtext_busy_cursor(`id(`proposal ) );
  1080.  
  1081.     // check for tab
  1082.  
  1083.     if (is (input, integer))
  1084.     {
  1085.         current_tab = (integer)input;
  1086.         load_matching_submodules_list();
  1087.         string proposal = "";
  1088.         foreach (string mod, submodules_presentation, ``{
  1089.         proposal = proposal + html[mod]:"";
  1090.         });
  1091.         display_proposal( proposal );
  1092.         get_submod_descriptions_and_build_menu ();
  1093.     }
  1094.  
  1095.     // check for hyperlink id
  1096.  
  1097.     if (is (input, string))
  1098.     {
  1099.         // get module for hyperlink id
  1100.         string submod = id2submod[input]:"";
  1101.  
  1102.         if (submod == "")
  1103.         {
  1104.         // also try hyperlinks
  1105.         submod = link2submod[input]:"";
  1106.         }
  1107.  
  1108.         if (submod != "")
  1109.         {
  1110.         // if submod is not the same as input id, provide id to the module
  1111.         map<string,any> additional_info = $[
  1112.             "has_next" : false,
  1113.         ];
  1114.  
  1115.         if (submod != input)
  1116.         {
  1117.             additional_info["chosen_id"] = input;
  1118.         }
  1119.  
  1120.         // Call AskUser() function.
  1121.         // This will trigger another call to make_proposal() internally.
  1122.         input = submod_ask_user( submod, additional_info );
  1123.  
  1124.         // The workflow_sequence doesn't get handled as a workflow sequence
  1125.         // so we have to do this special case here. Kind of broken.
  1126.         if (input == `finish)
  1127.             return `finish;
  1128.         }
  1129.     }
  1130.     else if (input == `rel_notes)
  1131.     {
  1132.         WFM::CallFunction("release_notes_popup", [] );
  1133.     }
  1134.     else if (input == `finish)
  1135.     {
  1136.         return `finish;
  1137.     }
  1138.     else if (input == `abort)
  1139.     {
  1140.         if ( Stage::initial () )
  1141.         {
  1142.         if (Popup::ConfirmAbort (`painless))
  1143.             return `abort;
  1144.         }
  1145.         else
  1146.         {
  1147.         if (Popup::ConfirmAbort (`incomplete))
  1148.             return `abort;
  1149.         }
  1150.     }
  1151.     else if (input == `reset_to_defaults
  1152.          && Popup::ContinueCancel(
  1153.                         // question in a popup box
  1154.                         _("Really reset everything to default values?") + "\n" +
  1155.                         // explain consequences of a decision
  1156.                         _("You will lose all changes.") ) )
  1157.     {
  1158.         make_proposal( true, false );    // force_reset
  1159.     }
  1160.     else if ( input == `skip || input == `dontskip )
  1161.     {
  1162.         if ((boolean) UI::QueryWidget (`id(`skip), `Value))
  1163.         {
  1164.         // User doesn't want to use any of the settings
  1165.         UI::ChangeWidget( `id(`proposal), `Value,
  1166.                   HTML::Newlines( 3 ) +
  1167.                   // message show when user has disabled the configuration
  1168.                   HTML::Para( _("Skipping configuration upon user request") )
  1169.                   );
  1170.         UI::ChangeWidget(`id(`menu), `Enabled, false );
  1171.         }
  1172.         else
  1173.         {
  1174.         // User changed his mind and wants the settings back - recreate them
  1175.         make_proposal( false, false );
  1176.         UI::ChangeWidget(`id(`menu), `Enabled, true );
  1177.         }
  1178.     }
  1179.     else if ( input == `next )
  1180.     {
  1181.             boolean skip = UI::WidgetExists(`id(`skip) ) ? (boolean) UI::QueryWidget(`id(`skip), `Value ) : true;
  1182.             boolean skip_blocker = UI::WidgetExists(`id(`skip) ) && skip;
  1183.         if (have_blocker && !skip_blocker)
  1184.         {
  1185.         // error message is a popup
  1186.         Popup::Error (_("The proposal contains an error that must be
  1187. resolved before continuing.
  1188. "));
  1189.         continue;
  1190.         }
  1191.  
  1192.         if ( Stage::stage () == "initial" )
  1193.         {
  1194.         input = WFM::CallFunction("inst_doit", [] );
  1195.         }
  1196.         // bugzilla #219097, #221571, yast2-update on running system
  1197.         else if ( Stage::stage () == "normal" && Mode::update () )
  1198.         {
  1199.         if (! confirmInstallation()) {
  1200.             y2milestone ("Update not confirmed, returning back...");
  1201.             input = nil;
  1202.         }
  1203.         }
  1204.  
  1205.             if ( input == `next )
  1206.             {
  1207.         // anything that needs to be done before
  1208.         // real installation starts
  1209.         PreInstallFunctions();
  1210.  
  1211.                 if ( ! skip )
  1212.                 {
  1213.                     write_settings();
  1214.                 }
  1215.  
  1216.         Wizard::HideReleaseNotesButton();
  1217.                 return `next;
  1218.             }
  1219.         }
  1220.     else if ( input == `back )
  1221.     {
  1222.         Wizard::HideReleaseNotesButton();
  1223.         if (Stage::initial ())
  1224.         Wizard::SetNextButton (`next, Label::NextButton ());
  1225.         return `back;
  1226.     }
  1227.  
  1228.     } // while input loop
  1229.  
  1230.  
  1231. // NOTREACHED
  1232.  
  1233. /* EOF */
  1234. }
  1235.