|
__Undefined = rawget( _G, "__Undefined") or {} |
|
setmetatable(__Undefined, { |
|
__toluacode = function(self, indent, pstr) |
|
local code = "__Undefined" |
|
if pstr then |
|
return pstr:append(code) |
|
else |
|
return code |
|
end |
|
end, |
|
__tostring = function(self) |
|
return "__Undefined__" |
|
end |
|
}) |
|
function Undefined() |
|
return __Undefined |
|
end |
|
|
|
if FirstLoad then |
|
GedMultiSelectAdapters = setmetatable({}, weak_keys_meta) |
|
end |
|
|
|
DefineClass.GedMultiSelectAdapter = { |
|
__parents = { "PropertyObject", "InitDone" }, |
|
__objects = {}, |
|
properties = false, |
|
property_merge_union = "any", |
|
} |
|
|
|
local dont_evaluate = { default = true, preset_filter = true, class_filter = true, setter = true, getter = true, dont_save = true } |
|
local special_treat = { max = Min, min = Max } |
|
local eval = prop_eval |
|
local function MergePropMeta(prop_accumulator, prop_meta, object) |
|
if prop_meta.no_edit and eval(prop_meta.no_edit, object, prop_meta) then |
|
return nil |
|
end |
|
|
|
if not prop_accumulator then |
|
prop_accumulator = {} |
|
for meta_name, value in pairs(prop_meta) do |
|
prop_accumulator[meta_name] = dont_evaluate[meta_name] and value or eval(value, object, prop_meta) |
|
end |
|
if prop_accumulator.default == nil then |
|
prop_accumulator.default = PropertyObject.GetDefaultPropertyValue(object, prop_meta.id, prop_meta) |
|
end |
|
prop_accumulator.__count = 1 |
|
else |
|
for meta_name, value in pairs(prop_meta) do |
|
value = dont_evaluate[meta_name] and value or eval(value, object, prop_meta) |
|
local acc_value = prop_accumulator[meta_name] |
|
local special_treat_fn = special_treat[meta_name] |
|
if special_treat_fn then |
|
prop_accumulator[meta_name] = special_treat_fn(acc_value, value) |
|
elseif not CompareValues(acc_value, value) then |
|
return nil |
|
end |
|
end |
|
prop_accumulator.__count = prop_accumulator.__count + 1 |
|
end |
|
return prop_accumulator |
|
end |
|
|
|
local function CalculatePropertyMetadata(objects) |
|
local prop_ids = {} |
|
local properties_accumulator = {} |
|
local prop_ids_with_mismatches = {} |
|
|
|
for _, object in ipairs(objects) do |
|
local properties = object:GetProperties() |
|
|
|
for _, prop_meta in ipairs(properties) do |
|
if not prop_ids_with_mismatches[prop_meta.id] then |
|
local acc = properties_accumulator[prop_meta.id] |
|
if not acc then |
|
prop_ids[#prop_ids+1] = prop_meta.id |
|
end |
|
local resulting_acc = MergePropMeta(acc, prop_meta, object) |
|
properties_accumulator[prop_meta.id] = resulting_acc |
|
if resulting_acc == nil then |
|
prop_ids_with_mismatches[prop_meta.id] = true |
|
end |
|
end |
|
end |
|
end |
|
|
|
|
|
return properties_accumulator, prop_ids |
|
end |
|
|
|
local function MultiselectAdapterGetProperties(objects, property_merge_union) |
|
local props = {} |
|
local metas, order = CalculatePropertyMetadata(objects) |
|
|
|
for _, prop_id in ipairs(order) do |
|
local meta = metas[prop_id] |
|
if meta then |
|
if property_merge_union == "any" or (meta.__count == #objects and property_merge_union == "all") then |
|
table.insert(props, meta) |
|
props[prop_id] = meta |
|
end |
|
end |
|
end |
|
return props |
|
end |
|
|
|
function GedMultiSelectAdapter:Init() |
|
local objects = self.__objects |
|
for i = #objects, 1, -1 do |
|
if not GedIsValidObject(objects[i]) then |
|
table.remove(objects, i) |
|
end |
|
end |
|
for _, obj in ipairs(objects) do |
|
Msg("GedBindObj", obj) |
|
end |
|
|
|
self:UpdatePropertyMetadatas() |
|
GedMultiSelectAdapters[self] = true |
|
end |
|
|
|
function GedMultiSelectAdapter:UpdatePropertyMetadatas() |
|
self.properties = MultiselectAdapterGetProperties(self.__objects, self.property_merge_union) |
|
|
|
|
|
for _, prop in ipairs(self.properties) do |
|
local editor, id = prop.editor, prop.id |
|
if editor == "nested_obj" or editor == "nested_list" then |
|
local value = self:GetProperty(id) |
|
if value ~= Undefined() and rawget(self, id) == nil then |
|
rawset(self, id, self:CopyNestedObjOrList(value)) |
|
end |
|
end |
|
end |
|
end |
|
|
|
function GedMultiSelectAdapter:CopyNestedObjOrList(value) |
|
local ret = false |
|
if value then |
|
if IsKindOf(value, "PropertyObject") then |
|
ret = value:Clone() |
|
else |
|
ret = {} |
|
for i, item in ipairs(value) do |
|
ret[i] = item:Clone() |
|
end |
|
end |
|
end |
|
return ret |
|
end |
|
|
|
function GedMultiSelectAdapter:CopyNestedPropValueToObjects(id) |
|
for _, obj in ipairs(self.__objects) do |
|
obj:SetProperty(id, self:CopyNestedObjOrList(self[id])) |
|
end |
|
end |
|
|
|
function GedMultiSelectAdapter:OnEditorSetProperty(prop_id, old_value, ged) |
|
local prop_meta = self:GetPropertyMetadata(prop_id) |
|
if prop_meta.editor == "nested_obj" or prop_meta.editor == "nested_list" then |
|
self:CopyNestedPropValueToObjects(prop_id) |
|
end |
|
end |
|
|
|
function GedMultiSelectAdapter:ClearNestedProperty(prop_id) |
|
local prop_meta = self:GetPropertyMetadata(prop_id) |
|
if prop_meta.editor == "nested_obj" or prop_meta.editor == "nested_list" then |
|
self[prop_id] = nil |
|
end |
|
end |
|
|
|
function GedMultiSelectAdapterObjModified(obj) |
|
if IsKindOf(obj, "GedMultiSelectAdapter") then |
|
obj:UpdatePropertyMetadatas() |
|
return |
|
end |
|
for adapter in pairs(GedMultiSelectAdapters) do |
|
for id, value in pairs(adapter) do |
|
if (rawequal(obj, value) or type(value) == "table" and table.find(value, obj)) and table.find(adapter.properties, "id", id) then |
|
adapter:CopyNestedPropValueToObjects(id) |
|
end |
|
end |
|
end |
|
end |
|
|
|
OnMsg.GedObjectModified = GedMultiSelectAdapterObjModified |
|
|
|
function GedMultiSelectAdapter:GetProperty(prop_id) |
|
local value = rawget(self, prop_id) |
|
if value ~= nil then |
|
return value |
|
end |
|
for _, obj in ipairs(self.__objects) do |
|
if GedIsValidObject(obj) then |
|
local new_val = GetProperty(obj, prop_id) |
|
if new_val ~= nil then |
|
if value ~= nil and not CompareValues(new_val, value) then |
|
return Undefined() |
|
end |
|
value = new_val |
|
end |
|
end |
|
end |
|
return value |
|
end |
|
|
|
function GedMultiSelectAdapter:GetDefaultPropertyValue(prop_id, prop_meta) |
|
if prop_meta then |
|
local value = rawget(prop_meta, "default") |
|
if value ~= nil then |
|
return value |
|
end |
|
end |
|
return self.properties[prop_id].default |
|
end |
|
|
|
function GedMultiSelectAdapter:ExecPropButton(root, prop_id, ged, func, param) |
|
SuspendObjModified("GedMultiSelectAdapter:ExecPropButton") |
|
local errs, undos = {}, {} |
|
for _, obj in ipairs(self.__objects) do |
|
if GedIsValidObject(obj) then |
|
local err, undo |
|
local prop_capture = GedPropCapture(obj) |
|
if type(func) == "function" then |
|
err, undo = func(obj, root, prop_id, ged, param) |
|
elseif obj:HasMember(func) then |
|
err, undo = obj[func](obj, root, prop_id, ged, param) |
|
elseif type(rawget(_G, func)) == "function" then |
|
err, undo = _G[func](root, obj, prop_id, ged, param) |
|
end |
|
|
|
local undop = GedCreatePropValuesUndoFn(obj, prop_capture) |
|
if type(err) == "string" then errs [#errs + 1] = err end |
|
if type(undo) == "function" then undos[#undos + 1] = undo end |
|
if type(undop) == "function" then undos[#undos + 1] = undop end |
|
end |
|
end |
|
ResumeObjModified("GedMultiSelectAdapter:ExecPropButton") |
|
|
|
return |
|
next(errs) and table.concat(errs, "\n"), |
|
#undos > 0 and function() for i = 1, #undos do undos[i]() end end or nil |
|
end |
|
|
|
function GedMultiSelectAdapter:OnEditorSelect(...) |
|
for _, obj in ipairs(self.__objects) do |
|
if GedIsValidObject(obj) and PropObjHasMember(obj, "OnEditorSelect") then |
|
obj:OnEditorSelect(...) |
|
end |
|
end |
|
end |