#nop vim: set filetype=tt:; /* 本文件属于 PaoTin++ 的一部分。 PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 */ #var lib_option[META] { {NAME} {全局选项管理} {DESC} {为全局选项提供了一个储存位置,并提供一些实用的 API。} {AUTHOR} {担子炮} }; VAR {全局选项字典} gOptions {}; load-lib storage; event.Define {option/changed} {无参} {$MODULE} {选项已更新。本事件只会唤醒和选项名称相对应的钩子}; #func {lib_option.Init} { #var gOptions {@storage.Get{global-options}}; #return {true}; }; ///=== { ///// 全局选项管理。 ///// ///// 全局选项本质上就是一些变量,这个模块主要是为了提供一些公共 API,和一个统 ///// 一的管理方式,能够把散落在各处的选项集中管理。 ///// ///// 选项具有作用域: ///// - share: 跨角色共享,修改后所有本机角色都会受到影响。 ///// - user: 按角色隔离,每个角色有自己的值,修改本角色不会影响其他角色。 ///// ///// 作用域在定义时指定,缺省为 user。可以在运行时通过 option.SetScope 调整。 ///// 具体细节请参考 HELP option.SetScope ///// // ## option.Define <名称> <数据类型> <中文含义> [<默认值>] [<作用域>] // 定义一个全局选项。选项被定义之后有一些公共 API 可供使用。 // 选项必须经过定义之后才能使用。选项的名称按照大骆驼风格命名。 // 作用域可选 share 或 user,缺省为 user。 // 目前支持 Bool, Number, String, Slist, Time 等类型的选项。 // }; #alias {option.Define} { #local option {%1}; #local type {%2}; #local meaning {%3}; #local default {%4}; #local scope {@default{%5;user}}; #if { "$option" == "" || "$type" == "" || "$meaning" == "" } { xtt.Usage option.Define; #return; }; #if { "$scope" != "{share|user}" } { xtt.Usage option.Define 作用域只能是 share 或 user。; #return; }; #if { "$gOptions[$option]" != "" } { #if { "$gOptions[$option][name]" != "$option" || "$gOptions[$option][type]" != "$type" || "$gOptions[$option][meaning]" != "$meaning" } { errLog 选项「$option」已存在,请检查代码。; }; #if { "$gOptions[$option][scope]" == "" } { #nop 兼容旧数据,补齐缺省作用域。; #var gOptions[$option][scope] {$scope}; }; #elseif { "$gOptions[$option][scope]" != "$scope" } { errLog 选项「$option」的作用域与已有定义不一致,请检查代码。; }; #return; }; #if { ! @option.isValidValue{$type;{$default}} } { #return; }; #var gOptions[$option] { {name} {$option} {type} {$type} {meaning} {$meaning} {value} {$default} {scope} {$scope} }; option.persist; }; ///=== { // ## option.List // 列出所有的选项。 // }; #alias {option.List} { option.ensure; #echo {}; #echo { <128>%-25s %-10s %-8s %-30s %+22s} {选项名称} {选项类型} {作用域} {选项含义} {选项当前值}; #draw Yellow scroll line 1 1 1 110; #local option {}; #foreach {*gOptions[]} {option} { #local value {$gOptions[$option][value]}; #if { "$gOptions[$option][type]" == "Time" } { #if { $value > 0 } { #format value {%t} {{%Y-%m-%d %H:%M:%S}{$value}}; }; }; #echo { %-25s %-10s %-8s %-30s %+22s} {$gOptions[$option][name]} {$gOptions[$option][type]} {$gOptions[$option][scope]} {$gOptions[$option][meaning]} {{$value}}; }; #echo {}; }; ///=== { // #@ option.Get <选项名称> // 获取选项的当前值。 // }; #func {option.Get} { #local {option} {%1}; #if { "$option" == "" } { xtt.Usage option.Get; #return {}; }; option.ensure; #if { "$gOptions[$option]" == "" } { errLog 不存在选项「$option」,请先定义后再使用。; #return {}; }; #return {$gOptions[$option][value]}; }; ///=== { // #@ option.Set <选项名称> <选项新值> // 设置选项的当前值,返回之前的旧值。 // }; #func {option.Set} { #local {option} {%1}; #local {value} {%2}; #if { "$option" == "" } { xtt.Usage option.Set; #return {}; }; option.ensure; #local old {$gOptions[$option]}; #if { "$old" == "" } { errLog 不存在选项「$option」,请先定义后再使用。; #return {}; }; #if { ! @option.isValidValue{$gOptions[$option][type];{$value}} } { #return {}; }; #if { "$old[value]" !== "$value" } { #var {gOptions[$option][value]} {$value}; option.persist; event.Emit {option/changed} {opt/$option/%*}; }; #return {$old[value]}; }; ///=== { // ## option.Set <选项名称> <选项值> // 设置选项的当前值。 // }; #alias {option.Set} { #local {option} {%1}; #local {value} {%2}; #local _ {@option.Set{$option;{$value}}}; }; ///=== { // ## option.Push <选项名称> <选项值> // 设置选项的当前值,并将旧值压入栈中。配合 option.Pop 可以实现选项值的回退。 // }; #alias {option.Push} { #local {option} {%1}; #local {value} {%2}; #if { "$option" == "" } { xtt.Usage option.Push; #return {}; }; option.ensure; #local old {$gOptions[$option]}; #if { "$old" == "" } { errLog 不存在选项「$option」,请先定义后再使用。; #return; }; #if { ! @option.isValidValue{$gOptions[$option][type];{$value}} } { #return; }; #var {gOptions[$option][value]} {$value}; #list gOptions[$option][stack] add {$old[value]}; option.persist; event.Emit {option/changed} {opt/$option/%*}; }; ///=== { // ## option.Pop <选项名称> <选项值> // 从选项的历史值中按照后进先出的原则弹出一个值,使之成为当前值。配合 option.Push 可以实现选项值的回退。 // }; #alias {option.Pop} { #local {option} {%1}; #if { "$option" == "" } { xtt.Usage option.Pop; #return {}; }; option.ensure; #local old {$gOptions[$option]}; #if { "$old" == "" } { errLog 不存在选项「$option」,请先定义后再使用。; #return; }; #if { &gOptions[$option][stack][] == 0 } { #return; }; #var {gOptions[$option][value]} {$old[stack][-1]}; #list gOptions[$option][stack] delete -1; option.persist; event.Emit {option/changed} {opt/$option/%*}; }; ///=== { // #@ option.IsEnable <选项名称> // 检查选项是否已被开启。 // }; #func {option.IsEnable} { #local option {%1}; #if { "$option" == "" } { xtt.Usage option.IsEnable; #return {}; }; #if { "$gOptions[$option]" == "" } { errLog 不存在选项「$option」,请先定义后再使用。; #return {}; }; #if { "$gOptions[$option][type]" != "Bool" } { errLog 只有 Bool 型变量才支持这个 API。; #return {}; }; #local value {@option.Get{$option}}; #return @isTrue{$value}; }; ///=== { // #@ option.IsDisable <选项名称> // 检查选项是否已被禁用。 // }; #func {option.IsDisable} { #local option {%1}; #if { "$option" == "" } { xtt.Usage option.IsDisable; #return {}; }; #if { "$gOptions[$option]" == "" } { errLog 不存在选项「$option」,请先定义后再使用。; #return {}; }; #if { "$gOptions[$option][type]" != "Bool" } { errLog 只有 Bool 型变量才支持这个 API。; #return {}; }; #local value {@option.Get{$option}}; #return @isFalse{$value}; }; ///=== { // #@ option.Toggle <选项名称> // 翻转选项的当前值。如果是启用则改为禁用;如果是禁用则改为启用。 // 本函数将返回之前的值。 // }; #func {option.Toggle} { #local {option} {%1}; #if { "$option" == "" } { xtt.Usage option.Toggle; #return {}; }; #if { "$gOptions[$option]" == "" } { errLog 不存在选项「$option」,请先定义后再使用。; #return {}; }; #if { "$gOptions[$option][type]" != "Bool" } { errLog 只有 Bool 型变量才支持这个 API。; #return {}; }; #local value {@option.Get{$option}}; #if { "$value" == "true" } { option.Disable $option; infoLog <169>选项「<139>$gOptions[$option][meaning]($option)<169>」 <119>已禁用。<299>; }; #elseif { "$value" == "false" } { option.Enable $option; infoLog <169>选项「<139>$gOptions[$option][meaning]($option)<169>」 <129>已启用。<299>; }; #return {$value}; }; ///=== { // ## option.Toggle <选项名称> // 翻转选项的当前值。如果是启用则改为禁用;如果是禁用则改为启用。 // }; #alias {option.Toggle} { #local {option} {%1}; #local _ {@option.Toggle{$option}}; }; ///=== { // #@ option.Scope <选项名称> // 获取选项的作用域,返回 share 或 user。 // 参见 option.SetScope // }; #func {option.Scope} { #local option {%1}; #if { "$option" == "" } { #return {}; }; #if { "$gOptions[$option]" == "" } { #return {}; }; #return {$gOptions[$option][scope]}; }; ///=== { // ## option.SetScope <选项名称> // 修改选项的作用域,并将选项数据从原来的存储迁移到新的存储。 // NOTE: 本命令会移动选项的数据,操作前请确认该选项的当前值。 // }; #alias {option.SetScope} { #local option {%1}; #local scope {%2}; #if { "$option" == "" || "$scope" == "" } { xtt.Usage option.SetScope; #return; }; #if { "$gOptions[$option]" == "" } { errLog 不存在选项「$option」,请先定义后再使用。; #return; }; #if { "$scope" != "{share|user}" } { xtt.Usage option.SetScope 作用域只能是 share 或 user。; #return; }; #local oldScope {$gOptions[$option][scope]}; #if { "$oldScope" == "$scope" } { #return; }; #var gOptions[$option][scope] {$scope}; option.persist; infoLog 选项「$gOptions[$option][meaning]($option)」的作用域已从 $oldScope 调整为 $scope。; }; ///=== { // ## option.Enable <选项名称> // 启用该选项。 // }; #alias {option.Enable} { #local {option} {%1}; option.Set $option {true}; }; ///=== { // ## option.Disable <选项名称> // 禁用该选项。 // }; #alias {option.Disable} { #local {option} {%1}; option.Set $option {false}; }; #func {@option.isValidValue} { #local type {%2}; #local value {%3}; #local types {Bool|String|Slist|Number|Time}; #if { "$type" != "{$types}" } { errLog 不能识别的选项类型: 「$type」,目前仅支持选项类型:{$types}; #return; }; #if { "$type" == "Bool" && "$value" != "{true|false}" } { errLog Bool 型的选项值只能是 true 或者 false。; #return 0; }; #else { #return 1; }; }; VAR {角色私有选项已加载} option-user-loaded {false}; #nop 内部命令,确保角色私有选项已经从磁盘加载。; #alias {option.ensure} { #if { @isTrue{$option-user-loaded} || "$user[id]" == "" } { #return; }; #local userOpts {@storage.GetUser{options}}; #local opt {}; #foreach {*userOpts[]} {opt} { #if { "$userOpts[$opt]" != "" } { #if { "$userOpts[$opt][scope]" == "" } { #nop 兼容旧数据,保留现有作用域。; #var userOpts[$opt][scope] {$gOptions[$opt][scope]}; }; #var gOptions[$opt] {$userOpts[$opt]}; }; }; #var option-user-loaded {true}; }; #nop 内部命令,将选项字典持久化到磁盘。 #nop global-options 键保存全部跨角色共享的选项; #nop options 键仅保存当前角色的私有选项,供不同角色隔离读取。; #alias {option.persist} { option.ensure; #if { "$user[id]" == "" } { #return; }; #local globalOpts {}; #local userOpts {}; #local opt {}; #foreach {*gOptions[]} {opt} { #if { "$gOptions[$opt][scope]" == "share" } { #var globalOpts[$opt] {$gOptions[$opt]}; }; #else { #var userOpts[$opt] {$gOptions[$opt]}; }; }; storage.Set {global-options} {$globalOpts}; storage.SetUser {options} {$userOpts}; };