library/cwm/src/modules/TablePopup.rb
# ***************************************************************************
#
# Copyright (c) 2002 - 2012 Novell, Inc.
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail,
# you may find current contact information at www.novell.com
#
# ***************************************************************************
# File: modules/TablePopup.ycp
# Package: Table/Popup dialogs backend
# Summary: Routines for Table/Popup interface
# Authors: Jiri Srain <jsrain@suse.cz>
#
# $Id$
#
require "yast"
module Yast
class TablePopupClass < Module
def main
Yast.import "UI"
textdomain "base"
Yast.import "CWM"
Yast.import "Label"
Yast.import "Mode"
Yast.import "Report"
# variables
# Item, that is the last selected
# Used to decide if selected item should be moved up or down if separator
# clicked
# Loss of contents is no problem
@previous_selected_item = nil
end
# local functions
# Get list of IDs of entries of the table
# @param [Hash{String => Object}] descr map table description map
# @return [Array] of IDs of the table
def getIdList(descr)
descr = deep_copy(descr)
toEval = Convert.convert(
Ops.get(descr, "ids"),
from: "any",
to: "list (map)"
)
return toEval.call(descr) if !toEval.nil?
[]
end
# Validate table options specifyign attributesA
# @param [Hash{String => Object}] attr a map of table attributes
# @return [Boolean] true if validation succeeded
def ValidateTableAttr(attr)
attr = deep_copy(attr)
types = {
"add_delete_buttons" => "boolean",
"edit_button" => "boolean",
"changed_column" => "boolean",
"up_down_buttons" => "boolean",
"unique_keys" => "boolean"
}
ret = true
Builtins.foreach(attr) do |k, v|
type = Ops.get(types, k)
if type.nil?
Builtins.y2error("Unknown attribute %1", k)
ret = false
else
ret = CWM.ValidateBasicType(v, type) && ret
end
end
ret
end
# Validate type of entry of the option description map
# Also checks option description maps if present
# @param [String] key string key of the map entry
# @param [Object] value any value of the map entry
# @param [String] widget any name of the widget/option
# @param [Boolean] popup boolean true if is option of a popup
# @return [Boolean] true if validation succeeded
def ValidateValueType(key, value, widget, popup)
value = deep_copy(value)
success = true
if popup
case key
when "init", "store", "cleanup"
success = Ops.is(value, "void (any, string)")
when "handle"
success = Ops.is(value, "void (any, string, map)") ||
Ops.is_symbol?(value)
when "validate_function"
success = Ops.is(value, "boolean (any, string, map)")
when "optional"
success = Ops.is_boolean?(value)
when "label_func"
success = Ops.is(value, "string (any, string)")
else
return CWM.ValidateValueType(key, value, widget)
end
else
success = case key
when "id2key" then Ops.is(value, "string (map, any)")
when "ids" then Ops.is(value, "list (map)")
when "option_delete" then Ops.is(value, "boolean (any, string)")
when "summary", "label_func" then Ops.is(value, "string (any, string)")
when "option_move" then Ops.is(value, "any (any, string, symbol)")
when "options" then Ops.is(value, "map <string, any>")
when "add_items" then Ops.is_list?(value)
end
end
if !success
Builtins.y2error(
"Wrong type of option %1 in description map of %2",
key,
widget
)
end
nil
end
# Validate the table description
# @param [Hash{String => Object}] descr a map containing the table description
# @return [Boolean] true if validation succeeded
def ValidateTableDescr(key, descr)
descr = deep_copy(descr)
ret = true
Builtins.foreach(descr) do |k, v|
ret = ValidateValueType(k, v, key, false) && ret
end
options = Ops.get_map(descr, "options", {})
Builtins.foreach(options) do |w_key, v|
des = Convert.convert(
v,
from: "any",
to: "map <string, map <string, any>>"
)
Builtins.foreach(des) do |group, d|
Builtins.y2error("Unknown entry in option %1: %2", w_key, group) if group != "table" && group != "popup"
Builtins.foreach(d) do |key2, value|
ValidateValueType(key2, value, w_key, true)
end
end
end
ret
end
# Get option key from the option id
# global only because of testsuites
# @param [Hash{String => Object}] descr map description of the table
# @param [Object] opt_id any id of the option
# @return [String] option key
def id2key(descr, opt_id)
descr = deep_copy(descr)
opt_id = deep_copy(opt_id)
if !opt_id.nil? && Ops.is_string?(opt_id) &&
Ops.greater_or_equal(Builtins.size(Convert.to_string(opt_id)), 7) &&
Builtins.substring(Convert.to_string(opt_id), 0, 7) == "____sep"
return "____sep"
end
toEval = Convert.convert(
Ops.get(descr, "id2key"),
from: "any",
to: "string (map, any)"
)
toEval.nil? ? Convert.to_string(opt_id) : toEval.call(descr, opt_id)
end
# Get option description map from the key
# global only because of testsuites
# @param [Hash{String => Object}] descr map description of the table
# @param [String] opt_key string option key
# @return [Hash] option description map
def key2descr(descr, opt_key)
descr = deep_copy(descr)
options = Ops.get_map(descr, "options", {})
opt_descr = Ops.get_map(options, opt_key, {})
# a copy wanted here
opt_descr = Builtins.add(opt_descr, "_cwm_key", opt_key)
# a deep copy
Ops.set(
opt_descr,
"table",
Builtins.add(Ops.get_map(opt_descr, "table", {}), "_cwm_key", opt_key)
)
Ops.set(
opt_descr,
"popup",
Builtins.add(Ops.get_map(opt_descr, "popup", {}), "_cwm_key", opt_key)
)
if Ops.get(opt_descr, ["popup", "label"]).nil?
Ops.set(
opt_descr,
["popup", "label"],
Ops.get_string(opt_descr, ["table", "label"], opt_key)
)
end
deep_copy(opt_descr)
end
# Update the option description map in order to contain handlers of
# all needed functions
# global only because of testsuites
# @param [Hash{String => Object}] opt_descr map option description map
# @param [Hash] fallbacks map of fallback handlers
# @return [Hash] updated option description map
def updateOptionMap(opt_descr, fallbacks)
opt_descr = deep_copy(opt_descr)
fallbacks = deep_copy(fallbacks)
# ensure that the submaps exist
Ops.set(opt_descr, "table", Ops.get_map(opt_descr, "table", {}))
Ops.set(opt_descr, "popup", Ops.get_map(opt_descr, "popup", {}))
Builtins.foreach(["init", "store"]) do |k|
if !Builtins.haskey(Ops.get_map(opt_descr, "popup", {}), k) &&
Builtins.haskey(fallbacks, k)
Ops.set(opt_descr, ["popup", k], Ops.get(fallbacks, k))
end
end
if !Builtins.haskey(Ops.get_map(opt_descr, "table", {}), "summary") &&
Builtins.haskey(fallbacks, "summary")
Ops.set(opt_descr, ["table", "summary"], Ops.get(fallbacks, "summary"))
end
if !Builtins.haskey(Ops.get_map(opt_descr, "table", {}), "label_func") &&
Builtins.haskey(fallbacks, "label_func")
Ops.set(
opt_descr,
["table", "label_func"],
Ops.get(fallbacks, "label_func")
)
end
if !Builtins.haskey(Ops.get_map(opt_descr, "table", {}), "changed") &&
Builtins.haskey(fallbacks, "changed")
Ops.set(opt_descr, ["table", "changed"], Ops.get(fallbacks, "changed"))
end
if Ops.get_string(opt_descr, "_cwm_key", "") == "____sep" &&
Ops.get_string(opt_descr, ["table", "label"], "") == ""
Ops.set(opt_descr, ["table", "label"], "--------------------")
end
deep_copy(opt_descr)
end
# Get the left column of the table
# @param [Object] opt_id any option id
# @param [Hash{String => Object}] opt_descr map option description map
# @return [String] text to the table
def tableEntryKey(opt_id, opt_descr)
opt_id = deep_copy(opt_id)
opt_descr = deep_copy(opt_descr)
opt_key = Ops.get_string(opt_descr, "_cwm_key", "")
label = Ops.get_string(
opt_descr,
["table", "label"],
Builtins.sformat("%1", opt_key)
)
if Builtins.haskey(Ops.get_map(opt_descr, "table", {}), "label_func")
label_func = Convert.convert(
Ops.get(opt_descr, ["table", "label_func"]),
from: "any",
to: "string (any, string)"
)
label = label_func.call(opt_id, opt_key)
end
label
end
# Get value to the table entry
# @param [Object] opt_id any option id
# @param [Hash{String => Object}] opt_descr map option description map
# @return [String] text to the table
def tableEntryValue(opt_id, opt_descr)
opt_id = deep_copy(opt_id)
opt_descr = deep_copy(opt_descr)
opt_key = Ops.get_string(opt_descr, "_cwm_key", "")
toEval = Convert.convert(
Ops.get(opt_descr, ["table", "summary"]),
from: "any",
to: "string (any, string)"
)
return toEval.call(opt_id, opt_key) if !toEval.nil?
""
end
# Realize if table entry was changed
# @param [Object] opt_id any option id
# @param [Hash{String => Object}] opt_descr map option description map
# @return [Boolean] true if was changed
def tableEntryChanged(opt_id, opt_descr)
opt_id = deep_copy(opt_id)
opt_descr = deep_copy(opt_descr)
opt_key = Ops.get_string(opt_descr, "_cwm_key", "")
toEval = Convert.convert(
Ops.get(opt_descr, ["table", "changed"]),
from: "any",
to: "boolean (any, string)"
)
return toEval.call(opt_id, opt_key) if !toEval.nil?
false
end
# Delete an item from the table
# Just a wrapper for module-specific function
# @param [Object] opt_id any option id
# @param [Hash{String => Object}] descr map table description map
# @return [Boolean] true if was really deleted
def deleteTableItem(opt_id, descr)
opt_id = deep_copy(opt_id)
descr = deep_copy(descr)
toEval = Convert.convert(
Ops.get(descr, "option_delete"),
from: "any",
to: "boolean (any, string)"
)
if nil != toEval
opt_key = id2key(descr, opt_id)
return toEval.call(opt_id, opt_key)
end
false
end
# Enable or disable the Delete and up/down buttons
# @param [Hash{String => Object}] descr map table description map
# @param [Hash{String => Object}] opt_descr map selected option description map
def updateButtons(descr, opt_descr)
descr = deep_copy(descr)
opt_descr = deep_copy(opt_descr)
if Ops.get_boolean(descr, ["_cwm_attrib", "add_delete_buttons"], true)
UI.ChangeWidget(
Id(:_tp_delete),
:Enabled,
Ops.get_boolean(opt_descr, ["table", "optional"], true)
)
end
if Ops.get_boolean(descr, ["_cwm_attrib", "edit_button"], true)
UI.ChangeWidget(
Id(:_tp_edit),
:Enabled,
!Ops.get_boolean(opt_descr, ["table", "immutable"], false)
)
end
if Ops.get_boolean(descr, ["_cwm_attrib", "up_down_buttons"], false)
UI.ChangeWidget(
Id(:_tp_up),
:Enabled,
Ops.get_boolean(opt_descr, ["table", "ordering"], true)
)
UI.ChangeWidget(
Id(:_tp_down),
:Enabled,
Ops.get_boolean(opt_descr, ["table", "ordering"], true)
)
end
nil
end
# Move table item up or down
# Just a wrapper for module-specific function
# @param [Object] opt_id any option id
# @param [Hash{String => Object}] descr map table description map
# @param [Symbol] dir symbol `up or `down (according to the button user pressed)
# @return [Object] new id of selected option, nil if wasn't reordered
def moveTableItem(opt_id, descr, dir)
opt_id = deep_copy(opt_id)
descr = deep_copy(descr)
toEval = Convert.convert(
Ops.get(descr, "option_move"),
from: "any",
to: "any (any, string, symbol)"
)
return toEval.call(opt_id, id2key(descr, opt_id), dir) if nil != toEval
nil
end
# Redraw completely the table
# @param [Hash{String => Object}] descr map description map of the whole table
# @param [Boolean] update_buttons boolean true if buttons status (enabled/disabled)
# should be updated according to currently selected item
def TableRedraw(descr, update_buttons)
descr = deep_copy(descr)
id_list = getIdList(descr)
@previous_selected_item = Ops.get(id_list, 0) if @previous_selected_item.nil?
entries = Builtins.maplist(id_list) do |opt_id|
opt_val = ""
opt_changed = false
opt_key = id2key(descr, opt_id)
opt_descr = key2descr(descr, opt_key)
opt_descr = updateOptionMap(
opt_descr,
Ops.get_map(descr, "fallback", {})
)
label = tableEntryKey(opt_id, opt_descr)
if opt_key != "____sep"
opt_val = tableEntryValue(opt_id, opt_descr)
opt_changed = tableEntryChanged(opt_id, opt_descr)
end
updateButtons(descr, opt_descr) if update_buttons && opt_id == @previous_selected_item
if Ops.get_boolean(descr, ["_cwm_attrib", "changed_column"], false)
next Item(
Id(opt_id),
opt_changed ? "*" : "",
label,
Builtins.sformat("%1", opt_val)
)
end
Item(Id(opt_id), label, Builtins.sformat("%1", opt_val))
end
UI.ChangeWidget(Id(:_tp_table), :Items, entries)
UI.SetFocus(Id(:_tp_table))
nil
end
# Displaye popup for option to edit choosing
# @param [Array] possible a list of strings or items of all possible options
# to provide
# @param [Boolean] editable boolean true means that it is possible to add non-listed
# options
# @param [Hash{String => Object}] descr a map table description map
# @return [String] option identifies, nil if canceled
def askForNewOption(possible, editable, descr)
possible = deep_copy(possible)
descr = deep_copy(descr)
do_sort = !Ops.get_boolean(descr, "add_items_keep_order", false)
possible = Builtins.sort(possible) if do_sort
val2key = {}
known_keys = {}
possible = Builtins.maplist(possible) do |p|
next if !Ops.is_string?(p)
opt_descr = key2descr(descr, Convert.to_string(p))
label = Ops.get_string(
opt_descr,
["table", "label"],
Builtins.sformat("%1", p)
)
Ops.set(known_keys, Convert.to_string(p), true)
Ops.set(val2key, label, Convert.to_string(p))
Item(Id(p), label)
end
widget = HBox(
HSpacing(1),
VBox(
VSpacing(1),
ComboBox(
Id(:optname),
editable ? Opt(:editable) : Opt(),
# combobox header
_("&Selected Option"),
possible
),
VSpacing(1),
HBox(
HStretch(),
PushButton(Id(:_tp_ok), Opt(:key_F10, :default), Label.OKButton),
HSpacing(1),
PushButton(Id(:_tp_cancel), Opt(:key_F9), Label.CancelButton),
HStretch()
),
VSpacing(1)
),
HSpacing(1)
)
UI.OpenDialog(widget)
begin
UI.SetFocus(Id(:optname))
ret = nil
option = nil
while ret != :_tp_ok && ret != :_tp_cancel
ret = UI.UserInput
option = Convert.to_string(UI.QueryWidget(Id(:optname), :Value)) if ret == :_tp_ok
end
ensure
UI.CloseDialog
end
return nil if ret == :_tp_cancel
return option if Ops.get(known_keys, option, false)
Ops.get(val2key, option, option)
end
# Display and handle the popup for option
# @param [Hash{String => Object}] option map one option description map that is modified in order
# to contain the option name and more percise option identification
# @return [Symbol] `_tp_ok or `_tp_cancel
def singleOptionEditPopup(option)
option = deep_copy(option)
opt_key = Ops.get_string(option, "_cwm_key", "")
opt_id = Ops.get(option, "_cwm_id")
label = Builtins.sformat(
"%1",
Ops.get_string(option, ["table", "label"], opt_key)
)
header = HBox(
# heading / label
Heading(_("Current Option: ")),
Label(label),
HStretch()
)
popup_descr = CWM.prepareWidget(Ops.get_map(option, "popup", {}))
widget = Ops.get_term(popup_descr, "widget", VBox())
help = Ops.get_string(popup_descr, "help", "")
help = "" if help.nil?
contents = HBox(
HSpacing(1),
VBox(
VSpacing(1),
Left(header),
VSpacing(1),
(help == "") ? VSpacing(0) : Left(Label(help)),
VSpacing((help == "") ? 0 : 1),
Left(ReplacePoint(Id(:value_rp), widget)),
VSpacing(1),
HBox(
HStretch(),
PushButton(Id(:_tp_ok), Opt(:key_F10, :default), Label.OKButton),
HSpacing(1),
PushButton(Id(:_tp_cancel), Opt(:key_F9), Label.CancelButton),
HStretch()
),
VSpacing(1)
),
HSpacing(1)
)
UI.OpenDialog(contents)
begin
if Ops.get(popup_descr, "init")
toEval = Convert.convert(
Ops.get(popup_descr, "init"),
from: "any",
to: "void (any, string)"
)
toEval.call(opt_id, opt_key)
end
ret = nil
while ret != :_tp_ok && ret != :_tp_cancel
event_descr2 = UI.WaitForEvent
event_descr2 = { "ID" => :_tp_ok } if Mode.test
ret = Ops.get(event_descr2, "ID")
if Ops.get(popup_descr, "handle")
toEval = Convert.convert(
Ops.get(popup_descr, "handle"),
from: "any",
to: "void (any, string, map)"
)
toEval.call(opt_id, opt_key, event_descr2)
end
next if ret != :_tp_ok
val_type = Ops.get_symbol(popup_descr, "validate_type")
if val_type == :function
toEval = Convert.convert(
Ops.get(popup_descr, "validate_function"),
from: "any",
to: "boolean (any, string, map)"
)
ret = nil if !toEval.nil? && !toEval.call(opt_id, opt_key, event_descr2)
elsif !CWM.validateWidget(popup_descr, event_descr2, opt_key)
ret = nil
end
end
if ret == :_tp_ok && Ops.get(popup_descr, "store")
toEval = Convert.convert(
Ops.get(popup_descr, "store"),
from: "any",
to: "void (any, string)"
)
toEval.call(opt_id, opt_key)
end
ensure
UI.CloseDialog
end
Convert.to_symbol(ret)
end
# functions
# Disable whole table
# @param [Hash{String => Object}] descr map table widget description map
def DisableTable(descr)
descr = deep_copy(descr)
UI.ChangeWidget(Id(:_tp_table), :Enabled, false)
UI.ChangeWidget(Id(:_tp_edit), :Enabled, false) if Ops.get_boolean(descr, ["_cwm_attrib", "edit_button"], true)
if Ops.get_boolean(descr, ["_cwm_attrib", "add_delete_buttons"], true)
UI.ChangeWidget(Id(:_tp_delete), :Enabled, false)
UI.ChangeWidget(Id(:_tp_add), :Enabled, false)
end
if Ops.get_boolean(descr, ["_cwm_attrib", "up_down_buttons"], false)
UI.ChangeWidget(Id(:_tp_up), :Enabled, false)
UI.ChangeWidget(Id(:_tp_down), :Enabled, false)
end
nil
end
# Enable whole table (except buttons that should be grayed according to
# currently selected table row
# @param [Hash{String => Object}] descr map table widget description map
def EnableTable(descr)
descr = deep_copy(descr)
UI.ChangeWidget(Id(:_tp_table), :Enabled, true)
UI.ChangeWidget(Id(:_tp_edit), :Enabled, true) if Ops.get_boolean(descr, ["_cwm_attrib", "edit_button"], true)
UI.ChangeWidget(Id(:_tp_add), :Enabled, false) if Ops.get_boolean(descr, ["_cwm_attrib", "add_delete_buttons"], true)
opt_id = UI.QueryWidget(Id(:_tp_table), :CurrentItem)
opt_key = id2key(descr, opt_id)
option_map = key2descr(descr, opt_key)
updateButtons(descr, option_map)
nil
end
# Initialize the displayed table
# @param [Hash{String => Object}] descr map description map of the whole table
# @param [String] key table widget key
def TableInit(descr, key)
descr = deep_copy(descr)
@previous_selected_item = nil
Ops.set(descr, "_cwm_key", key)
TableRedraw(descr, true)
nil
end
# Handle the event that happened on the table
# @param [Hash{String => Object}] descr map description of the table
# @param [String] key table widget key
# @param [Hash] event_descr map event to handle
# @return [Symbol] modified event if needed
def TableHandle(descr, key, event_descr)
descr = deep_copy(descr)
event_descr = deep_copy(event_descr)
event_id = Ops.get(event_descr, "ID")
UI.SetFocus(Id(:_tp_table))
if event_id == :_tp_table && (Ops.get_string(event_descr, "EventReason", "") == "Activated" &&
Ops.get_string(event_descr, "EventType", "") == "WidgetEvent" &&
UI.WidgetExists(Id(:_tp_edit)))
event_id = :_tp_edit
end
case event_id
when :_tp_edit, :_tp_add
opt_key = nil
opt_id = nil
case event_id
when :_tp_add
add_unlisted = Ops.get_boolean(descr, "add_unlisted", true)
if !add_unlisted &&
Builtins.size(Ops.get_list(descr, "add_items", [])) == 1
opt_key = Ops.get_string(descr, ["add_items", 0], "")
else
add_opts = Ops.get_list(descr, "add_items", [])
ids = getIdList(descr)
present = Builtins.maplist(ids) { |i| id2key(descr, i) }
if !Ops.get_boolean(descr, ["_cwm_attrib", "unique_keys"], false)
present = Builtins.filter(present) do |i|
opt_descr = key2descr(descr, i)
!Ops.get_boolean(opt_descr, ["table", "optional"], true)
end
end
add_opts = Builtins.filter(add_opts) do |o|
!Builtins.contains(present, o)
end
selected = false
until selected
opt_key = askForNewOption(add_opts, add_unlisted, descr)
return nil if opt_key.nil?
if Builtins.contains(present, opt_key)
Report.Error(
# error report
_("The selected option is already present.")
)
else
selected = true
end
end
end
return nil if opt_key.nil?
when :_tp_edit
opt_id = UI.QueryWidget(Id(:_tp_table), :CurrentItem)
opt_key = id2key(descr, opt_id)
end
option_map = key2descr(descr, opt_key)
toEval = Ops.get(option_map, ["table", "handle"])
if !toEval.nil?
# if (is (toEval, symbol))
if Ops.is(toEval, "symbol (any, string, map)")
toEval_c = Convert.convert(
Ops.get(option_map, ["table", "handle"]),
from: "any",
to: "symbol (any, string, map)"
)
ret2 = toEval_c.call(opt_id, opt_key, event_descr)
return ret2 if ret2 != :_tp_normal
else
ret2 = Convert.to_symbol(toEval)
return ret2
end
end
Ops.set(option_map, "_cwm_id", opt_id)
Ops.set(option_map, "_cwm_key", opt_key)
# add generic handlers if needed
option_map = updateOptionMap(
option_map,
Ops.get_map(descr, "fallback", {})
)
ret = singleOptionEditPopup(option_map)
if ret == :_tp_ok
case event_id
when :_tp_add
TableInit(descr, key)
when :_tp_edit
column = descr.fetch("_cwm_attrib", {}).fetch("changed_column", false) ? 2 : 1
UI.ChangeWidget(
Id(:_tp_table),
term(:Item, opt_id, column),
tableEntryValue(opt_id, option_map)
)
# also redraw the key field as it can be changed
column = Ops.subtract(column, 1)
UI.ChangeWidget(
Id(:_tp_table),
term(:Item, opt_id, column),
tableEntryKey(opt_id, option_map)
)
if Ops.get_boolean(descr, ["_cwm_attrib", "changed_column"], false)
UI.ChangeWidget(
Id(:_tp_table),
term(:Item, opt_id, 0),
tableEntryChanged(opt_id, option_map) ? "*" : ""
)
end
end
end
when :_tp_delete
opt_id = UI.QueryWidget(Id(:_tp_table), :CurrentItem)
TableInit(descr, key) if deleteTableItem(opt_id, descr)
when :_tp_table
opt_id = UI.QueryWidget(Id(:_tp_table), :CurrentItem)
key2 = id2key(descr, opt_id)
if key2 == "____sep"
id_list = getIdList(descr)
previous_index = 0
if !@previous_selected_item.nil?
previous_index = -1
Builtins.find(id_list) do |e|
previous_index = Ops.add(previous_index, 1)
e == @previous_selected_item
end
end
current_index = -1
Builtins.find(id_list) do |e|
current_index = Ops.add(current_index, 1)
e == opt_id
end
# rubocop:disable Lint/DuplicateBranch
# rubocop here wrongly detect logic in if conditions to decide step direction
step = if current_index == 0
1
elsif Ops.add(current_index, 1) == Builtins.size(id_list)
-1
elsif Ops.greater_or_equal(current_index, previous_index)
1
else
-1
end
# rubocop:enable Lint/DuplicateBranch
new_index = Ops.add(current_index, step)
opt_id = Ops.get(id_list, new_index)
UI.ChangeWidget(Id(:_tp_table), :CurrentItem, opt_id)
end
@previous_selected_item = deep_copy(opt_id)
opt_descr = key2descr(descr, id2key(descr, opt_id))
updateButtons(descr, opt_descr)
when :_tp_up, :_tp_down
opt_id = UI.QueryWidget(Id(:_tp_table), :CurrentItem)
opt_id = moveTableItem(opt_id, descr, (event_id == :_tp_up) ? :up : :down)
if nil != opt_id
TableRedraw(descr, false)
UI.ChangeWidget(Id(:_tp_table), :CurrentItem, opt_id)
opt_descr = key2descr(descr, id2key(descr, opt_id))
updateButtons(descr, opt_descr)
end
end
nil
end
# Wrapper for TableInit using CWM::GetProcessedWidget () for getting
# widget description map
# @param [String] key any widget key
def TableInitWrapper(key)
TableInit(CWM.GetProcessedWidget, key)
nil
end
# Wrapper for TableHandle using CWM::GetProcessedWidget () for getting
# widget description map
# @param [String] key any widget key
# @param [Hash] event_descr map event description map
# @return [Symbol] return value for wizard sequencer or nil
def TableHandleWrapper(key, event_descr)
event_descr = deep_copy(event_descr)
TableHandle(CWM.GetProcessedWidget, key, event_descr)
end
# Get the map with the table widget
# @param [Hash{String => Object}] attrib map table attributes
# @param [Hash{String => Object}] widget_descr map widget description map of the table, will be
# unioned with the generated map
# @return [Hash] table widget
def CreateTableDescr(attrib, widget_descr)
attrib = deep_copy(attrib)
widget_descr = deep_copy(widget_descr)
ValidateTableAttr(attrib)
add_button = if Ops.get_boolean(attrib, "add_delete_buttons", true)
PushButton(Id(:_tp_add), Opt(:key_F3), Label.AddButton)
else
HSpacing(0)
end
edit_button = if Ops.get_boolean(attrib, "edit_button", true)
PushButton(Id(:_tp_edit), Opt(:key_F4), Label.EditButton)
else
HSpacing(0)
end
delete_button = if Ops.get_boolean(attrib, "add_delete_buttons", true)
PushButton(Id(:_tp_delete), Opt(:key_F5), Label.DeleteButton)
else
HSpacing(0)
end
table_header = if Ops.get_boolean(attrib, "changed_column", false)
Header(
# table header, shortcut for changed, keep very short
_("Ch."),
# table header
_("Option"),
# table header
_("Value")
)
else
Header(
# table header
_("Option"),
# table header
_("Value")
)
end
replace_point = ReplacePoint(Id(:_tp_table_repl), HSpacing(0))
# help 1/4
help = _(
"<p><b><big>Editing the Settings</big></b><br>\n" \
"To edit the settings, choose the appropriate\n" \
"entry of the table then click <b>Edit</b>.</p>"
)
if Ops.get_boolean(attrib, "add_delete_buttons", true)
# help 2/4, optional
help = Ops.add(
help,
_(
"<p>To add a new option, click <b>Add</b>. To remove\nan option, select it and click <b>Delete</b>.</p>"
)
)
end
if Ops.get_boolean(attrib, "changed_column", false)
# help 3/4, optional
help = Ops.add(
help,
_(
"<P>The <B>Ch.</B> column of the table shows \nwhether the option was changed.</P>"
)
)
end
if Ops.get_boolean(attrib, "up_down_buttons", false)
# help 4/4, optional
help = Ops.add(
help,
_(
"<p>To reorder the options, select an option\n" \
"and use <b>Up</b> and <b>Down</b> to move it up or down\n" \
"in the list.</p>"
)
)
end
up_down = if Ops.get_boolean(attrib, "up_down_buttons", false)
VBox(
VStretch(),
# push button
PushButton(Id(:_tp_up), _("&Up")),
# push button
PushButton(Id(:_tp_down), _("&Down")),
VStretch()
)
else
HSpacing(0)
end
ret = Convert.convert(
Builtins.union(
{
"custom_widget" => HBox(
HSpacing(2),
VBox(
HBox(
Table(
Id(:_tp_table),
Opt(:immediate, :notify, :keepSorting),
table_header,
[]
),
up_down
),
HBox(
add_button,
edit_button,
delete_button,
HStretch(),
replace_point
)
),
HSpacing(2)
),
"_cwm_attrib" => attrib,
"widget" => :custom,
"help" => help,
"_cwm_do_validate" => fun_ref(
method(:ValidateTableDescr),
"boolean (string, map <string, any>)"
)
},
widget_descr
),
from: "map",
to: "map <string, any>"
)
if !Builtins.haskey(ret, "init")
Ops.set(
ret,
"init",
fun_ref(method(:TableInitWrapper), "void (string)")
)
end
if !Builtins.haskey(ret, "handle")
Ops.set(
ret,
"handle",
fun_ref(method(:TableHandleWrapper), "symbol (string, map)")
)
end
deep_copy(ret)
end
publish function: :id2key, type: "string (map <string, any>, any)"
publish function: :key2descr, type: "map <string, any> (map <string, any>, string)"
publish function: :updateOptionMap, type: "map <string, any> (map <string, any>, map)"
publish function: :tableEntryChanged, type: "boolean (any, map <string, any>)"
publish function: :deleteTableItem, type: "boolean (any, map <string, any>)"
publish function: :updateButtons, type: "void (map <string, any>, map <string, any>)"
publish function: :askForNewOption, type: "string (list, boolean, map <string, any>)"
publish function: :singleOptionEditPopup, type: "symbol (map <string, any>)"
publish function: :DisableTable, type: "void (map <string, any>)"
publish function: :EnableTable, type: "void (map <string, any>)"
publish function: :TableInit, type: "void (map <string, any>, string)"
publish function: :TableHandle, type: "symbol (map <string, any>, string, map)"
publish function: :TableInitWrapper, type: "void (string)"
publish function: :TableHandleWrapper, type: "symbol (string, map)"
publish function: :CreateTableDescr, type: "map <string, any> (map <string, any>, map <string, any>)"
end
TablePopup = TablePopupClass.new
TablePopup.main
end