ACC SHELL
/**
* File: ep-dialogs.ycp
* Package: yast2-storage
* Summary: Expert Partitioner
* Authors: Arvin Schnell <aschnell@suse.de>
*
* This file must only be included in other Expert Partitioner files ("ep-*.ycp").
*/
{
textdomain "storage";
include "partitioning/custom_part_lib.ycp";
string MiniWorkflowStepFormatMountHelptext()
{
// helptext
string helptext = _("<p>First, choose whether the partition should be
formatted and the desired file system type.</p>");
// helptext
helptext = helptext + _("<p>Changing the encryption on an existing
volume will delete all data on it.</p>");
// helptext
helptext = helptext + _("<p>Then, choose whether the partition should
be mounted and enter the mount point (/, /boot, /usr, /var, etc.).</p>");
return helptext;
}
symbol MiniWorkflowStepFormatMount(map<string, any> &orig_data)
{
map<string, any> data = orig_data;
y2milestone("MiniWorkflowStepFormatMount data:%1", data);
//retrieve all filesystems
map<symbol, map<symbol, any> > all_filesystems = FileSystems::GetAllFileSystems(true, true);
void ChangeWidgetIfExists( symbol wid, symbol property, any value)
{
if ( UI::WidgetExists(`id(wid)) )
UI::ChangeWidget(`id(wid), property, value);
}
list <string> ProposeMountpoints( symbol used_fs, string current_mp )
{
map <symbol, any> fs_data = all_filesystems[used_fs]: $[];
//not much choice for swap partitions :)
if( used_fs == `swap )
{
return fs_data[ `mountpoints ]:[];
}
//otherwise, ask notUsedMountPoint, it will filter out
//mountpoint already in use + add current mountpoint
else
{
list <string> not_used = notUsedMountpoints( Storage::GetTargetMap(), fs_data[ `mountpoints]:[]);
return ( list <string> ) union( [current_mp], not_used );
}
}
// disable Options p.b. if no fs options can be set
// disable Encrypt box if fs doesn't support encryption
void EnableDisableFsOpts( symbol used_fs )
{
map <symbol, any> fs_data = all_filesystems[used_fs]: $[];
UI::ChangeWidget(`id(`fs_options), `Enabled, fs_data[`options]:[] != []);
ChangeWidgetIfExists(`crypt_fs, `Enabled, fs_data[`crypt]:true);
}
boolean do_format = data["format"]:false;
symbol used_fs = data["used_fs"]:`unknown;
symbol default_crypt_fs = data["type"]:`unknown == `loop ? `luks : `none;
boolean crypt_fs = data["enc_type"]:default_crypt_fs != `none;
boolean orig_crypt_fs = crypt_fs;
string mount = data["mount"]:"";
boolean do_mount = mount != "";
boolean AskPassword()
{
y2milestone( "AskPassword data:%1", data );
boolean ret = crypt_fs;
if( ret )
ret = do_format || orig_crypt_fs!=crypt_fs || do_mount;
if( ret && !do_format )
{
string key = (data["type"]:`unknown != `loop) ? (data["device"]:"error") : (data["fpath"]:"error");
ret = Storage::NeedCryptPwd(key);
}
return( ret );
}
/* MiniWorkflowStepPartitionSize data:
$["create":true,
"cyl_size":8225280,
"device":"/dev/sda1",
"disk_device":"/dev/sda",
"new":true,
"slots":$[`primary:[1, 65]],
"type":`primary]
*/
//Supply some reasonable defaults for newly created partitions
//and mark it for formatting, too
if (data["new"]:false && !data["formatmount_proposed"]:false)
{
data["formatmount_proposed"] = true;
//propose new mountpoint and filesystem
string mount_point_proposal = SingleMountPointProposal( );
used_fs = Partitions::DefaultFs();
//special case for boot partition
if (mount_point_proposal == Partitions::BootMount())
used_fs = Partitions::DefaultBootFs();
data["format"] = true;
data["fsid"] = Partitions::fsid_native;
data["ori_fsid"] = Partitions::fsid_native;
data["used_fs"] = used_fs;
//set globals
do_format = true;
mount = mount_point_proposal;
do_mount = mount != "";
if (data["type"]:`unknown == `loop)
do_mount = true;
}
term tmp1 = `Empty();
if (contains([`primary, `extended, `logical], data["type"]:`none))
{
tmp1 = `VBox(`id(`do_not_format_attachment),
FsidComboBox(data, FileSystems::GetAllFileSystems(true, true))
);
}
list mountpoints = ProposeMountpoints( used_fs, mount );
term contents = `HVSquash(`VStackFrames(
`FrameWithMarginBox(_("Formatting Options"),
`RadioButtonGroup(`id(`format),
`VBox(
`LeftRadioButtonWithAttachment(`id(`do_format), `opt(`notify),
_("Format partition"),
`VBox(`id(`do_format_attachment),
FileSystemsComboBox(data, all_filesystems)
)),
`VSpacing(0.45),
`LeftRadioButtonWithAttachment(`id(`do_not_format), `opt(`notify),
_("Do not format partition"),
tmp1),
`VSpacing(0.45),
CryptButton(data)
)
)
),
`FrameWithMarginBox(_("Mounting Options"),
`RadioButtonGroup(`id(`mount),
`VBox(
`LeftRadioButtonWithAttachment(`id(`do_mount), `opt(`notify),
_("Mount partition"),
`VBox(
`id(`do_mount_attachment),
`ComboBox(`id(`mount_point), `opt(`editable, `hstretch, `notify),
_("Mount Point"), mountpoints),
`PushButton(`id(`fstab_options), `opt(`hstretch),
// button text
_("Fs&tab Options..."))
)),
`VSpacing(0.45),
`LeftRadioButton(`id(`do_not_mount), `opt(`notify),
_("Do not mount partition"))
)
)
)
)
);
MiniWorkflow::SetContents(Greasemonkey::Transform(contents), MiniWorkflowStepFormatMountHelptext());
MiniWorkflow::SetLastStep(!AskPassword());
UI::ChangeWidget(`id(`format), `Value, do_format ? `do_format : `do_not_format);
UI::ChangeWidget(`id(`do_format_attachment), `Enabled, do_format);
//not there in RAID/LVM/loop configuration (#483789)
ChangeWidgetIfExists( `do_not_format_attachment, `Enabled, !do_format );
if( do_format)
EnableDisableFsOpts(used_fs);
//not there on s390s
ChangeWidgetIfExists( `crypt_fs, `Value, crypt_fs );
UI::ChangeWidget(`id(`mount), `Value, do_mount ? `do_mount : `do_not_mount);
UI::ChangeWidget(`id(`do_mount_attachment), `Enabled, do_mount);
UI::ChangeWidget(`id(`mount_point), `Value, mount);
symbol widget = nil;
data = HandlePartWidgetChanges(true, widget, all_filesystems, orig_data, data);
repeat
{
widget = MiniWorkflow::UserInput();
if (widget != `back && widget != `abort)
{
data = HandlePartWidgetChanges(false, widget, all_filesystems, orig_data, data);
}
switch (widget)
{
//user has chosen different filesystem
case `fs:
{
used_fs = (symbol) UI::QueryWidget(`id(`fs), `Value);
//retrieve info about fs user has selected
map used_fs_data = all_filesystems[used_fs]:$[];
y2milestone("Selected filesystem details %1", used_fs_data);
if (used_fs != data["used_fs"]:`none)
{
//set file system type
data["used_fs"] = used_fs;
data = filter(string key, any value, data, { return key != "fs_options"; });
//set file system ID (and update the File System ID widget
//that is - FsidComboBox)
data["fsid"] = used_fs_data[`fsid]: Partitions::fsid_native;
UI::ChangeWidget( `id(`fsid_point), `Value, used_fs_data[`fsid_item]:"");
//suggest some nice mountpoints if user wants to mount this partition
if (do_mount)
{
UI::ChangeWidget(`id(`mount_point), `Items, ProposeMountpoints( used_fs, mount ));
}
}
break;
}
case `crypt_fs:
crypt_fs = (boolean) UI::QueryWidget(`id(`crypt_fs), `Value);
MiniWorkflow::SetLastStep(!AskPassword());
break;
/* already done in HandlePartWidgetChanges
case `fs_options:
map fs_options = FileSystems::DefaultFormatOptions(data);
if (size(fs_options) > 0 && !haskey(data, "fs_options"))
data["fs_options"] = FileSystems::DefaultFormatOptions(data);
map filesystems = FileSystems::GetAllFileSystems(true, true);
data["fs_options"] = FileSystemOptions(data["fs_options"]:$[], filesystems[data["used_fs"]:`unknown]:$[]);
break;
*/
/* already done in HandlePartWidgetChanges
case `fstab_options:
string fstopt = FileSystems::DefaultFstabOptions(data);
if (size(fstopt) > 0 && !haskey(data, "fstopt"))
data["fstopt"] = fstopt;
data = FstabOptions(data, data);
break;
*/
case `do_format:
do_format = (boolean) UI::QueryWidget(`id(`do_format), `Value);
data["used_fs"] = (symbol) UI::QueryWidget(`id(`fs), `Value);
UI::ChangeWidget(`id(`do_format_attachment), `Enabled, true);
ChangeWidgetIfExists(`do_not_format_attachment, `Enabled, false);
EnableDisableFsOpts( data["used_fs"]:`none);
UI::SetFocus(`id(`fs));
MiniWorkflow::SetLastStep(!AskPassword());
break;
case `do_not_format:
do_format = (boolean) UI::QueryWidget(`id(`do_format), `Value);
UI::ChangeWidget(`id(`do_format_attachment), `Enabled, false);
ChangeWidgetIfExists(`do_not_format_attachment, `Enabled, true);
MiniWorkflow::SetLastStep(!AskPassword());
break;
case `fsid_point:
// TODO
break;
case `do_mount:
do_mount = true;
UI::ChangeWidget(`id(`do_mount_attachment), `Enabled, true);
UI::SetFocus(`id(`mount_point));
//propose mountpoints
//UI::ChangeWidget(`id(`mount_point), `Items, ProposeMountpoints( used_fs, mount ));
MiniWorkflow::SetLastStep(!AskPassword());
break;
case `do_not_mount:
do_mount = false;
UI::ChangeWidget(`id(`do_mount_attachment), `Enabled, false);
MiniWorkflow::SetLastStep(!AskPassword());
break;
case `next:
do_format = (boolean) UI::QueryWidget(`id(`do_format), `Value);
crypt_fs = (boolean) UI::QueryWidget(`id(`crypt_fs), `Value);
do_mount = (boolean) UI::QueryWidget(`id(`do_mount), `Value);
mount = (string) UI::QueryWidget(`id(`mount_point), `Value);
// TODO: checks
//crypt-file specific checks
if (data["type"]:`unknown == `loop)
{
//is encrypt fs checked?
if (!crypt_fs)
{
// error popup
Popup::Error(_("Crypt files must be encrypted."));
UI::ChangeWidget(`id(`crypt_fs), `Value, true);
UI::SetFocus(`id(`crypt_fs));
widget = `again;
continue;
}
//enforce formatting the crypt-file
if (data["create_file"]:false && !do_format)
{
// error popup
Popup::Error(_("You chose to create the crypt file, but did not specify
that it should be formatted. This does not make sense.
Also check the format option.
"));
UI::ChangeWidget(`id(`do_format), `Value, true);
UI::ChangeWidget(`id(`do_format_attachment), `Enabled, true);
UI::SetFocus(`id(`fs));
widget = `again;
continue;
}
//enforce specifying mountpoint
if (!do_mount)
{
// error popup
Popup::Error(_("Crypt files require a mount point."));
UI::ChangeWidget(`id(`do_mount), `Value, true);
UI::ChangeWidget(`id(`do_mount_attachment), `Enabled, true);
UI::SetFocus(`id(`mount_point));
widget = `again;
continue;
}
}
if (do_mount)
{
map ret_mp = CheckOkMount(data["device"]:"error", orig_data, data);
if (!ret_mp["ok"]:false)
{
if (ret_mp["field"]:`none != `none)
UI::SetFocus(`id(ret_mp["field"]:`none));
widget = `again;
continue;
}
}
if (do_format)
{
if (!check_ok_fssize(data["size_k"]:0, data))
{
widget = `again;
continue;
}
}
break;
}
}
until (widget == `abort || widget == `back || widget == `next);
if (widget == `next)
{
if( crypt_fs )
data["enc_type"] = data["format"]:false ? `luks : `twofish;
else
data["enc_type"] = `none;
data["format"] = do_format;
data["mount"] = do_mount ? mount : "";
if (!data["format"]:false)
data = filter(string key, any value, data, { return key != "fs_options"; });
if (contains([`primary, `extended, `logical], data["type"]:`unknown))
if (data["fsid"]:0 != orig_data["fsid"]:0)
data["change_fsid"] = true;
if (!AskPassword())
widget = `finish;
orig_data = data;
}
y2milestone("MiniWorkflowStepFormatMount data:%1 ret:%2", data, widget);
return widget;
}
string MiniWorkflowStepPasswordHelptext(map<string, any> data)
{
integer min_pw_len = data["format"]:false ? 8 : 1;
boolean empty_pw_allowed = EmptyCryptPwdAllowed(data);
// helptext
string helptext = _("<p>
Keep in mind that this file system is only protected when it is not
mounted. Once it is mounted, it is as secure as every other
Linux file system.
</p>");
if (empty_pw_allowed)
{
if (data["used_fs"]:`unknown == `swap)
// helptext
helptext = helptext + _("<p>
The file system used for this volume is swap. You may leave the crypt password
empty but then the swap device cannot be used for hibernating (suspend to
disk).
</p>
");
else
// helptext
helptext = helptext + _("<p>
This mount point corresponds to a temporary filesystem like /tmp or /var/tmp.
You may leave the crypt password empty. If you do this, the system will create
a random password at system startup for you. This means, you will lose all
data on these filesystems at system shutdown.
</p>
");
}
// helptext
helptext = helptext + _("<p>
If you forget your password, you will lose access to the data on your file system.
Choose your password carefully. A combination of letters and numbers
is recommended. To ensure the password was entered correctly,
enter it twice.
</p>
");
// helptext, %1 is replaced by integer
helptext = helptext + sformat(_("<p>
You must distinguish between uppercase and lowercase. A password should have at
least %1 characters and, as a rule, not contain any special characters
(e.g., letters with accents or umlauts).
</p>
"), min_pw_len);
// helptext
helptext = helptext + _("<p>
Do not forget this password!
</p>");
return helptext;
}
symbol MiniWorkflowStepPassword(map<string, any> &data)
{
y2milestone("MiniWorkflowStepPassword data:%1", data);
integer min_pw_len = data["format"]:false ? 8 : 1;
boolean empty_pw_allowed = EmptyCryptPwdAllowed(data);
boolean two_pw = data["format"]:false || isempty(data["mount"]:"");
string label = "";
if( two_pw )
{
label = _("All data previously present on the volume will be lost!");
label = label + "\n";
label = label + _("Don't forget what you enter here!");
label = label + "\n";
}
if (empty_pw_allowed)
label = label + _("Empty password allowed.");
term ad = `Empty();
if( two_pw )
ad = `Password( `id(`pw2), `opt(`hstretch),
// Label: get same password again for verification
// Please use newline if label is longer than 40 characters
_("Reenter the password for &verification:"), "");
term contents = `HVSquash(`FrameWithMarginBox(_("Password"),
`VBox(
`Password(`id(`pw1), `opt(`hstretch),
// Label: get password for user root
// Please use newline if label is longer than 40 characters
_("&Enter a password for your file system:"), ""),
ad,
`VSpacing(0.5),
`Left(`Label(label))
)
)
);
MiniWorkflow::SetContents(Greasemonkey::Transform(contents), MiniWorkflowStepPasswordHelptext(data));
MiniWorkflow::SetLastStep(true);
string password = "";
symbol widget = nil;
//don't put those inside the loop - they'd be reset after each unsuccesful try
UI::ChangeWidget(`id(`pw1), `Value, "");
if (two_pw)
UI::ChangeWidget(`id(`pw2), `Value, "");
string dev = (data["type"]:`unknown != `loop) ? data["device"]:""
: data["fpath"]:"";
repeat
{
widget = MiniWorkflow::UserInput();
if (widget == `next)
{
password = (string) UI::QueryWidget(`id(`pw1), `Value);
string tmp = password;
if (two_pw)
tmp = (string) UI::QueryWidget(`id(`pw2), `Value);
boolean need_verify = !data["format"]:false && !isempty(data["mount"]:"");
if (!Storage::CheckEncryptionPasswords(password, tmp, min_pw_len, empty_pw_allowed) ||
(need_verify && !Storage::CheckCryptOk(dev, password, false, false)) )
{
UI::SetFocus(`id(`pw1) );
widget = `again;
}
}
}
until (widget == `abort || widget == `back || widget == `next);
if (widget == `next)
{
Storage::SetCryptPwd(dev, password);
widget = `finish;
}
y2milestone("MiniWorkflowStepPassword data:%1 ret:%2", data, widget);
return widget;
}
boolean DlgResize(map <string, any> &data, map <string, any> disk)
{
/*
* This resize dialog is simple but avoids several problems faced
* before:
*
* - Stripped digits in a bargraph (bnc #445590)
*
* - Impossible to resize to maximal size (bnc #373744, #442318,
* #456816)
*
* - Changing dialog size (bnc #460382)
*
* If somebody wants fancy stuff like slider or bargraph it's a
* feature request.
*/
map<string, map> target_map = Storage::GetTargetMap();
map possible = Storage::IsResizable(data);
if (!data["format"]:false && !possible["shrink"]:false && !possible["extend"]:false)
{
// popup text
Popup::Message(_("
You cannot resize the selected partition because the file system
on this partition does not support resizing.
"));
return false;
}
integer cyl_size = 0;
integer free_cyl_after = 0;
string device = data["device"]:"error";
symbol used_fs = data["used_fs"]:`none;
integer used_k = FileSystems::MinFsSizeK(used_fs);
if (!data["format"]:false)
{
if (used_fs != `swap)
{
map free_data = Storage::GetFreeSpace(device, used_fs, true);
if (isempty(free_data) || !free_data["ok"]:false)
{
y2error("Failed to retrieve FreeSpace %1, filesystem %2", device, data["used_fs"]:`none);
//FIXME: Really?
Popup::Error(sformat(_("Partition %1 cannot be resized\nbecause the filesystem seems to be inconsistent"), device));
return false;
}
used_k = Integer::Max([ used_k, free_data["used"]:0 / 1024 ]);
}
}
integer min_free_k = 10*1024;
integer size_k = data["size_k"]:0;
// minimal and maximal size for volume
integer min_size_k = Integer::Min([ used_k + min_free_k, size_k ]);
integer max_size_k = 0;
string heading = "";
switch (data["type"]:`unknown)
{
case `primary:
case `logical:
{
// Heading for dialog
heading = sformat(_("Resize Partition %1"), device);
cyl_size = disk["cyl_size"]:1;
integer free_cyl_before = 0;
Storage::FreeCylindersAroundPartition(device, free_cyl_before, free_cyl_after);
min_size_k = Integer::Max([ min_size_k, cyl_size / 1024 ]);
max_size_k = size_k + (cyl_size * free_cyl_after) / 1024;
}
break;
case `lvm:
{
// Heading for dialog
heading = sformat(_("Resize Logical Volume %1"), device);
min_size_k = Integer::Max([ min_size_k, disk["pesize"]:0 / 1024 ]);
max_size_k = size_k + (disk["pe_free"]:0 * disk["pesize"]:0) / 1024;
}
break;
}
// size_k + min_size_k could be > max_size_k
min_size_k = Integer::Min([ min_size_k, max_size_k ]);
y2milestone("used_k:%1 size_k:%2", used_k, size_k);
y2milestone("min_size_k:%1 max_size_k:%2", min_size_k, max_size_k);
term infos = `VBox(`Left(`Label(sformat(_("Current size: %1"), Storage::KByteToHumanString(size_k)))));
if (used_fs != `swap && !data["format"]:false)
infos = add(infos, `Left(`Label(sformat(_("Currently used: %1"), Storage::KByteToHumanString(used_k)))));
term contents = `HVSquash(
// frame heading
`FrameWithMarginBox(_("Size"),
`RadioButtonGroup(`id(`size),
`VBox(
`LeftRadioButton(`id(`max_size), `opt(`notify),
// radio button text, %1 is replaced by size
sformat(_("Maximum Size (%1)"), Storage::KByteToHumanString(max_size_k))),
`LeftRadioButton(`id(`min_size), `opt(`notify),
// radio button text, %1 is replaced by size
sformat(_("Minimum Size (%1)"), Storage::KByteToHumanString(min_size_k))),
// radio button text
`LeftRadioButtonWithAttachment(`id(`custom_size), `opt(`notify), _("Custom Size"),
`VBox(`id(`custom_size_attachment),
`MinWidth(15, `InputField(`id(`custom_size_input),
`opt(`shrinkable), _("Size")))
)),
`VSpacing(1),
infos
)
)
));
UI::OpenDialog(
`VBox(
`Left(`Heading(heading)),
Greasemonkey::Transform(contents),
`VSpacing(1.0),
`ButtonBox(
`PushButton(`id(`help), `opt(`helpButton), Label::HelpButton()),
`PushButton(`id(`cancel), `opt(`cancelButton), Label::CancelButton()),
`PushButton(`id(`ok), `opt(`default, `okButton), Label::OKButton())
)
)
);
// help text
string help_text = _("<p>Choose new size.</p>");
UI::ChangeWidget(`help, `HelpText, help_text);
UI::ChangeWidget(`id(`size), `Value, `max_size);
UI::ChangeWidget(`id(`custom_size_attachment), `Enabled, false);
symbol widget = nil;
integer old_size_k = size_k;
do {
widget = (symbol) UI::UserInput();
switch (widget)
{
case `max_size:
{
UI::ChangeWidget(`id(`custom_size_attachment), `Enabled, false);
break;
}
case `min_size:
{
UI::ChangeWidget(`id(`custom_size_attachment), `Enabled, false);
break;
}
case `custom_size:
{
UI::ChangeWidget(`id(`custom_size_attachment), `Enabled, true);
UI::SetFocus(`id(`custom_size_input));
break;
}
case `ok:
{
switch ((symbol) UI::QueryWidget(`id(`size), `Value))
{
case `max_size:
{
size_k = max_size_k;
break;
}
case `min_size:
{
size_k = min_size_k;
break;
}
case `custom_size:
{
string tmp = (string) UI::QueryWidget(`id(`custom_size_input), `Value);
if (!Storage::HumanStringToKByteWithRangeCheck(tmp, size_k, min_size_k, max_size_k))
{
// error popup, %1 and %2 are replaced by sizes
Popup::Error(sformat(_("The size entered is invalid. Enter a size between %1 and %2."),
Storage::KByteToHumanString(min_size_k),
Storage::KByteToHumanString(max_size_k)));
widget = `again;
continue;
}
break;
}
}
if (size_k != old_size_k)
{
string mountpoint = data["inactive"]:false ? "" : data["mount"]:"";
boolean lvm = data["type"]:`unknown == `lvm;
//1 - ask & be interactive, 2 - we are on lvm, 3 - cyl.diff, 4 - filesystem, 5 - mountpoint
if (!CheckResizePossible(false, lvm, size_k - old_size_k, used_fs, mountpoint))
{
//FIXME: To check whether the part. can be resized only
//after user tries to do that is stupid - in some cases
//we can tell beforehand, thus user should never get to this
//point (e.g. when the partition is mounted)
y2error("Resizing the partition is not possible");
widget = `again;
continue;
}
switch (data["type"]:`unknown)
{
case `primary:
case `logical:
integer num_cyl = tointeger(1024.0 * size_k / cyl_size + 0.5);
num_cyl = Integer::Clamp(num_cyl, 1, data["region", 1]:0 + free_cyl_after);
data["region", 1] = num_cyl;
break;
case `lvm:
data["size_k"] = size_k;
break;
}
}
break;
}
case `cancel:
break;
}
} while (widget != `cancel && widget != `ok);
UI::CloseDialog();
return widget == `ok;
}
void DisplayCommandOutput(string command)
{
// TODO: maybe use LogView.ycp, but here we want to wait until the command has finished
// TODO: better error handling
UI::OpenDialog(`VBox(
// label for log view
`MinWidth(60, `LogView(`id(`log), sformat(_("Output of %1"), command), 15, 0)),
`PushButton(`opt(`default), Label::CloseButton())
));
map tmp = (map) SCR::Execute(.target.bash_output, command);
string lines = tmp["stderr"]:"" != "" ? tmp["stderr"]:"" : tmp["stdout"]:"";
UI::ChangeWidget(`id(`log), `Value, lines);
UI::UserInput();
UI::CloseDialog();
}
void RescanDisks()
{
UI::OpenDialog(`opt(`decorated),
// popup text
`MarginBox(2, 1, `Label(_("Rescanning disks..."))));
Storage::ReReadTargetMap();
UI::CloseDialog();
}
boolean ConfirmRecursiveDelete(string device, list<string> devices, string headline, string text_before,
string text_after)
{
term button_box = `ButtonBox (
`PushButton(`id(`yes), `opt(`okButton), Label::DeleteButton() ),
`PushButton(`id(`no_button), `opt(`default, `cancelButton), Label::CancelButton())
);
map display_info = UI::GetDisplayInfo();
boolean has_image_support = display_info["HasImageSupport"]:false;
term layout =
`VBox (
`VSpacing (0.4),
`HBox (
has_image_support ? `Top (`Image(Icon::IconPath ("question"))) : `Empty(),
`HSpacing (1),
`VBox (
`Left (`Heading (headline)),
`VSpacing (0.2),
`Left( `Label( text_before ) ),
`VSpacing (0.2),
`Left(`RichText(HTML::List(sort(devices)))),
`VSpacing (0.2),
`Left(`Label( text_after)),
button_box
)
)
);
UI::OpenDialog(layout);
symbol ret = (symbol) UI::UserInput();
UI::CloseDialog();
return ret == `yes;
}
}
ACC SHELL 2018