ACC SHELL
/**
* File:
* StorageDialog.ycp
*
* Module:
* Partitioning
*
* Summary:
* Display and handle main dialog.
*
* Authors:
* Sven Schober (sschober@suse.de)
*
* $Id: StorageDialog.ycp 2810 2008-05-29 14:49:59Z sschober $
*/
{
textdomain "autoinst";
import "Label";
import "Popup";
import "String";
import "AutoinstStorage";
import "AutoinstPartPlan";
include "autoinstall/common.ycp";
include "autoinstall/tree.ycp";
include "autoinstall/DriveDialog.ycp";
include "autoinstall/VolgroupDialog.ycp";
include "autoinstall/PartitionDialog.ycp";
//Storage::GetTargetMap(); // initialize libstorage
define boolean startsWith( string haystack, string needle ){
return substring( haystack, 0, size(needle)) == needle;
}
/**
* Displayed when tree is empty
*/
define term EmptyDialog(){
return `Label(`id(`treeinspect),_("Nothing selected"));
}
/**
* Top level event handler
*
* Catches all events not handled by the currently active
* subdialog (one of: Drive, Partition or LVM).
*
* The general idea is (like with dispatchMenuEvent()):
*
* When the user clicks on a button (Add {Drive, Partition,
* LVM}, Remove):
*
* 1. the check() function on the currently active dialog is
* called. If there were any changes to the settings the user
* is asked if these should be saved (this does not happen for
* delete events).
*
* 2. for the 'add'-type button events the according new() dialog
* function is called; for delete events the according delete()
* function is called.
*/
define void StorageEventHandler(){
y2milestone( "DriveEventHandler(): current event: '%1'", currentEvent);
if( is( currentEvent, map) ){
if( `addDrive == currentEvent["WidgetID"]:`Empty ){
/* 'Add Drive' button clicked */
y2milestone( "'Add Drive' button clicked" );
callDialogFunction( currentDialog, `check );
updateCurrentDialog("drive");
callDialogFunction( currentDialog, `new );
}
else if( `addPart == currentEvent["WidgetID"]:`Empty ){
/* 'Add Partition' button clicked */
y2milestone( "'Add Partition' button clicked" );
callDialogFunction( currentDialog, `check );
string item = currentTreeItem();
if( "" != item ){
stack[`which] = stripTypePrefix( item );
}
else{
y2error( "No item selected.");
}
updateCurrentDialog("part");
callDialogFunction( currentDialog, `new );
}
else if( `addVG == currentEvent["WidgetID"]:`Empty ){
/* 'Add Volume Group' button clicked */
y2milestone( "'Add Volume Group' button clicked" );
callDialogFunction( currentDialog, `check );
updateCurrentDialog("volgroup");
callDialogFunction( currentDialog, `new );
}
else if( `delete == currentEvent["WidgetID"]:`Empty ){
/* 'Delete' button clicked */
y2milestone( "'Delete' button clicked" );
string item = currentTreeItem();
if( "" != item ){
stack[`which] = stripTypePrefix( item );
callDialogFunction( updateCurrentDialog(getTypePrefix(item)), `delete );
if( 0 == AutoinstPartPlan::getDriveCount() ){
/* if last drive was removed display new drive dialog */
updateCurrentDialog("drive");
callDialogFunction( currentDialog, `new );
}
else{
/* else display the now selected item */
item = currentTreeItem();
stack[`which] = stripTypePrefix( item );
callDialogFunction( updateCurrentDialog(getTypePrefix(item)), `display );
}
}
else{
y2milestone( "No item selected." );
}
}
else if( `apply == currentEvent["WidgetID"]:`Empty ){
y2milestone("Apply button clicked in dialog '%1'. Storing settings.", currentDialog );
callDialogFunction( currentDialog, `store );
/* reflect changes to AutoinstPartPlan in tree widget */
AutoinstPartPlan::updateTree();
}
}
}
/*
* TODO: this distinction between tree- and widget
* events might be superflous and could be genralized.
*/
/*
* Generic tree event dispatching
*/
define void dispatchMenuEvent( map event ){
string item = currentTreeItem();
if( "" == item ){
/* User deselected current tree item. Happens when she clicks on empty line. */
y2milestone("Deselection event. Nothing happens, deliberately.");
}
else{
/* User selected a new tree item. */
/* 1. check for changed settings in current dialog */
callDialogFunction( currentDialog, `check);
/* 2. display new dialog */
stack[`which] = stripTypePrefix( item );
callDialogFunction(updateCurrentDialog(getTypePrefix(item)),`display );
}
}
/*
* Generic widget event dispatching
*
* First, the dialog specific event handler gets a shot
* afterwards the toplevel one kicks in.
*/
define void dispatchWidgetEvent( map event ){
any eventHandler = currentDialog[`eventHandler]:nil;
currentEvent = event;
eval( eventHandler );
if( size(currentEvent) != 0 ){
/*
* the subdialog event handler didn't process the event, so
* we call the toplevel handler
*/
StorageEventHandler();
}
}
/**
* Main Partitioning Dialog
* @return symbol
*/
define symbol StorageDialog()
{
term contents =
`HBox(
`HWeight(33,
`VBox(
`Tree(`id(`tree),`opt(`notify),_("Partition p&lan"), []),
`VSpacing(0.3),
`VBox(
`HBox(
`HSpacing(1),
`HWeight(50, `PushButton(`id(`addDrive), _("Add D&rive")) ),
`HWeight(50, `PushButton(`id(`addPart), _("Add &Partition"))),
`HSpacing(1)
),
`HBox(
`HSpacing(1),
`HWeight(50, `PushButton(`id(`addVG), _("Add &Volume Group")) ),
`HWeight(50, `PushButton(`id(`delete), _("&Delete"))),
`HSpacing(1)
)
),
`VSpacing(1)
)
),
`HSpacing(1),
`HWeight(66,
`VBox(
`VSpacing(1),
`Frame( "",
`Top(
`ReplacePoint(`id(replacement_point), EmptyDialog())
)
),
`VSpacing(1),
`HBox(
`Left(`PushButton(`id(`back), Label::BackButton()) ),
`HStretch(),
`Right(`PushButton(`id(`next), Label::FinishButton()))
)
)
),
`HSpacing(1)
);
UI::OpenDialog( `opt(`defaultsize), contents );
AutoinstPartPlan::updateTree();
/* if there is no drive in partition plan, the user surely
* wants to create one
*/
updateCurrentDialog("drive");
if( 0 == AutoinstPartPlan::getDriveCount() ){
callDialogFunction( currentDialog, `new );
}
else{
string item = currentTreeItem();
stack[`which] = stripTypePrefix( item );
callDialogFunction( currentDialog, `display );
}
while(true){
map event = UI::WaitForEvent();
string item = currentTreeItem();
y2milestone( "Got event: '%1'", event );
y2milestone( "Selected tree item: '%1'", item);
/*
* MAIN EVENT HANDLING
*
* General Event Handling Idea:
*
* There is a global map "dialogs" that maps dialog types to a
* dialog map, which in turn stores call backs for stuff like
* displaying the dialog contents, eventhandling, getting and setting
* of settings,...
*
* This architecture avoids long if-elseif-cascades.
*/
symbol event_id = `Empty;
if( is( event["ID"]:nil, symbol ) ){
event_id = event["ID"]:`Empty;
}
if( event_id == `abort ||
event_id == `back ||
event_id == `cancel){
return event_id;
}
else if( event_id == `next ||
event_id == `finish ){
callDialogFunction( currentDialog, `check );
if( AutoinstPartPlan::checkSanity() ){
/* Only return successfully if plan is sane */
return event_id;
}
}
else if( event["EventType"]:"" == "MenuEvent" ||
/* ncurses sends following kind of event: */
event["WidgetID"]:`Empty == sTree ){
/* Tree Event */
dispatchMenuEvent(event);
}
else if( event["EventType"]:"" == "WidgetEvent" ){
/* Button was pressed */
dispatchWidgetEvent(event);
}
}
return (symbol)`Empty;
}
// EOF
}
ACC SHELL 2018