File size: 7,403 Bytes
b6a38d7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
__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 = {}, -- try to prevent name collisions; we store nested objs/lists directly in the object
properties = false,
property_merge_union = "any", -- or "all"
}
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 = {} -- keep the order of the properties.
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
-- convert to standard prop table
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)
-- creates copies of any nested_obj/nested_list properties in the object for Ged to edit
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 |