--------------------------------------------- -- Base class for R2 components / features -- --------------------------------------------- -- NB : throughout the file 'this' is used instead of the lua 'self' to denote the fact -- that method are not called on the class definition, but on the instances of this class baseClass = -- not local here because of the implementation ... { ---------------------- -- CLASS PROPERTIES -- ---------------------- BaseClass = "", -- Name of the base class Version = 0, Name = "BaseClass", -- Name of the class BuildPropertySheet = true, -- True if objects of this class should be editable by using a generic property sheet -- Setting to 'true' will cause the framework to create the property sheet ui at launch Menu="", -- ui path of the contextual menu for this class -- DisplayerUI = "", -- name of the C++ class that displays this object in the ui DisplayerUIParams = "", -- parameters passed to the ui displayer when it is created -- DisplayerProperties = "R2::CDisplayerLua", -- 'lua' type of displayer takes the name of a lua function that must return the class of the displayer DisplayerPropertiesParams = "propertySheetDisplayer", -- name of the function that build the displayer that update the property sheet. -- TreeIcon="", -- icon to be displayed in the tree (or a method returning it) PermanentTreeIcon="", -- icon to be displayed in the tree if in permanent content (or a method returning it) SelectBarType = "", -- type in select bar -- rollout header for the generic property sheet (a string giving the xml code for the header) PropertySheetHeader = nil, ------------------- -- CLASS METHODS -- ------------------- ClassMethods = {}, -------------------------- -- INSTANCES PROPERTIES -- -------------------------- -- * Unlike class properties above, they are created in each instance of the class. They are directly accessible in the objects. -- * They are C++ objects exposed through the metatable mechanism -- * They are *READ-ONLY*. They should be modified by network commands like r2.requestSetNode and the like -- To store client side transcient values, use the native property 'User' (detailed below) instead -- TODO complete doc (available types, widgets ...) Prop = { {Name="InstanceId", Type="String", WidgetStyle="StaticText", Category="Advanced", Visible=false }, }, -- 'VIRTUAL' PROPERTIES (NOT IMPLEMENTED) -- Not really properties of this class, but 'shortcuts' to other, real properties -- The virtual properties are use by the property sheet to display properties than are jnot directly in the object, -- but that can be found in another place. (son object for example, or object that require an indirection) -- each virtual prop should provide a 'getPath' function which takes the InstanceId of the instance in which it is contained as its parameter -- It should return a couple : realOwnerInstanceId, realPropName (arrays not supported yet) -- an observer is placed on the real property to take its changes in account in the property sheet -- IMPORTANT: the getPath function will be called each frame to see if observers should be removed -- and placed on another target -- Example : indirection to fictive string property 'Toto' in the Scenario --VirtualProp = --{ -- {Name="InstanceId", Type="String", WidgetStyle="StaticText", Category="Advanced", Visible=true, -- getPath = function() -- return r2.Scenario.InstanceId, "Toto" -- end, -- }, --}, --------------------------------- -- NATIVE / SPECIAL PROPERTIES -- --------------------------------- -- They are implemented in C++ (thanks to the lua metatables) -- Parent : (R/O) : The direct parent of this object. If parent object is in a table, returns the table -- ParentInstance : (R/O) : Next parent of this object with an InstanceId. In object is contained in a property that -- is a table, the object containing the table will be returned, not the table -- IndexInParent : (R/O) : If parent is a table, then return index into it, -1 otherwise -- User : Read/Write lua table attached to the object. The 'User' field itself is R/O This is the place to store client side -- edition variables. -- Size : (R/O) : If object is a table, returns its size, nil otherwise -- DisplayerUI : (R/O) : for instances : returns reference to the ui displayer. May be nil. -- In the case where the field 'DisplayerUI' of this class definition id 'R2::CDisplayerLua', it would return -- the displayer object created by a call to the function defined in 'DisplayerUIParams' (in this class definition) -- DisplayerVisual : (R/O) : Same as DisplayerUI but for 'in scene' displayer -- DisplayerProperties : (R/O) : Same as DisplayerUI but for the properties displayer -- Selectable : (R/W) : default is true. When false, the object can't be selected in the scene. This flag is local to the instance (ancestor state not inherited) -- SelectableFromRoot : (R/O) : True if this object and also its ancestor are selectable ------------ -- EVENTS -- ------------ -- Events that are sent to the displayers, are also sent to instances that hold those displayers : -- By default they are not handled -- To handle, one shouldd redefine : -- function baseClass.onCreate(this) -- function baseClass.onErase(this) -- etc ... -- see r2_ui_displayers.lua for details } --------------------- -- GENERAL METHODS -- --------------------- -- Methods are defined in the class definition and are nevertheless callable on instances as follow : -- instance:methodName(params ...) -- In the class, the method would be defined as follow: -- methodName = function(this, param1, param2 ...) ... some code ... end -- 'this' will be filled at runtime by a reference on the instance on which the method is called. -- Method calling is possible thanks to the metamethod mechanism (in this case it is implemented in C++) -- Calling a method is in fact equivalent to doing the following : -- r2:getClass(instance).methodName(instance, param1, param2 ..) --------------------------------------- -- TYPE / CLASS / PROPERTIES METHODS -- --------------------------------------- --------------------------------------------------------------------------------------------------------- -- return a reference to the class of this object function baseClass.getClass(this) return r2:getClass(this) end --------------------------------------------------------------------------------------------------------- -- get description of a property (as found in the 'Prop' table of the class definition) from its name function baseClass.getPropDesc(this, propName) return this:getClass().NameToProp[propName] end --------------------------------------------------------------------------------------------------------- -- return name of the parent class function baseClass.getClassName(this) return this.Class end --------------------------------------------------------------------------------------------------------- -- test if object is of the given class (or derived from the class) -- param 'class' should be a string identifying the class function baseClass.isKindOf(this, className) assert( type(this) == "userdata") local currClass = this:getClass() while currClass do if currClass.Name == className then return true end currClass = r2.Classes[currClass.BaseClass] end return false end --------------------------------------------------------------------------------------------------------- -- return a 'this' of the base class -- Use this to access a method defined in a base class from a derived class -- -- example : this:delegate():doThis() -- Call the doThis function in the parent class -- -- Expected behavior is the same than with C++ : -- Call from a delegated pointer is static -- any further call is the call chain is polymorphic -- Calls to delegate can be chained -- NB : this function shouldn't be redefined in derived classes (the delegation mechanism uses a pointer on this function) --function baseClass.delegate(this) -- return __baseClassImpl.delegate(this) -- implementation defined in "r2_base_class_private.lua" --end --------------------------------------------------------------------------------------------------------- -- Get actual C++ "this" for this object. Because of the delegation mechanism, this may be a raw C++ object -- or a lua table that performs the delegation -- OBSOLETE, TO REMOVE function baseClass.getRawThis(this) -- return __baseClassImpl.getRawThis(this) -- implementation defined in "r2_base_class_private.lua" return this end --------------------------------------------------------------------------------------------------------- -- compare current "this", with another "this" pointer -- This should be the standard way to compare instance in memory because 'this' may sometime be a userdata -- (raw C++ pointer to internal C++ object), or a table (delegated 'this' pointer) -- OBSOLETE, TO REMOVE function baseClass.isSameObjectThan(this, other) --if this:isKindOf("Act") then -- breakPoint() --end --return this:getRawThis() == other:getRawThis() return this == other end --------------------------------------------------------------------------------------------------------- -- Helper : Return world position (that is, absolute position). By default, object deriving from the base class have no position function baseClass.getWorldPos() return { x = 0, y = 0, z = 0 } end --------------------------------------------------------------------------------------------------------- -- When adding content, pionneer have a limited budget. This method gives the 'cost' of this object (0 by default) --function baseClass.getUsedQuota(this) -- return 0 --end ------------------- -- SCENARIO COST -- ------------------- --------------------------------------------------------------------------------------------------------- -- See wether this element has a cost in the scenario function baseClass.hasScenarioCost(this) return false end --------------------------------------------------------------------------------------------------------- -- get local cost cached in object function baseClass.getLocalCost(this) return defaulting(this.User.Cost, 0) end --------------------------------------------------------------------------------------------------------- -- set local cost in object function baseClass.setLocalCost(this, cost) this.User.Cost = cost end --------------------------------------------------------------------------------------------------------- -- get local static cost cached in object function baseClass.getLocalStaticCost(this) return defaulting(this.User.StaticCost, 0) end --------------------------------------------------------------------------------------------------------- -- set local static cost in object function baseClass.setLocalStaticCost(this, cost) this.User.StaticCost = cost end function baseClass.getStaticObjectCost(this) return 0 end function baseClass.getAiCost(this) return 0 end ---------------------- -- OBJECT HIERARCHY -- ---------------------- --------------------------------------------------------------------------------------------------------- -- append all sub-content that is "kind of" 'kind' to 'destTable' -- NB : this is very SLOW!!! please use iterators instead (see r2:enumInstances) function baseClass.appendInstancesByType(this, destTable, kind) assert(type(kind) == "string") if this.CompareClass and this.CompareClass == true then if this.Class == kind then if destTable == nil then dumpCallStack(1) end table.insert(destTable, this:getRawThis()) end elseif this:isKindOf(kind) then if destTable == nil then dumpCallStack(1) end table.insert(destTable, this:getRawThis()) end end --------------------------------------------------------------------------------------------------------- -- Append all instances rooted at this object (including this one) function baseClass.getSons(this, destTable) r2:exploreInstanceTree(this, destTable) end --------------------------------------------------------------------------------------------------------- -- Search first ancestor of the wanted kind (that is of class 'className' or a derived class) function baseClass.getParentOfKind(this, className) local parent = this.ParentInstance while parent do assert(parent.isKindOf) if parent:isKindOf(className) then return parent end parent = parent.ParentInstance end return nil end --------------------------------------------------------------------------------------------------------- -- Search parent until an act is found function baseClass.getParentAct(this) return this:getParentOfKind("Act") end --------------------------------------------------------------------------------------------------------- -- Search parent until a scenario is found function baseClass.getParentScenario(this) return this:getParentOfKind("Scenario") end --------------------------------------------------------------------------------------------------------- -- See if hits object is inserted in the default feature (that is : single npcs, bot objects, roads etc. with no enclosing group or feature) function baseClass.isInDefaultFeature(this) return this.ParentInstance:isKindOf('DefaultFeature') end -------------------------- -- UI METHODS / DISPLAY -- -------------------------- --------------------------------------------------------------------------------------------------------- -- Called by the contextual menu/toolbar when the 'delete' option is chosen by the user -- on THIS client -- This is the place to perform additionnal deletion tasks -- Example : a vertex may want to delete its containing region when there are 2 vertices left only -- default -> just call 'r2.requestEraseNode' function baseClass.onDelete(this) if this.User.DeleteInProgress == true then return end this.User.DeleteInProgress = true this:setDeleteActionName() this:selectNext() r2.requestEraseNode(this.InstanceId, "", -1) r2.requestEndAction() end -- helper : add "delete : name_of_the_thing_being_deleted" in the action historic as the name of the delete action that is about -- to be done function baseClass.setDeleteActionName(this) r2.requestNewAction(concatUCString(i18n.get("uiR2EDDeleteAction"), this:getDisplayName())) end --------------------------------------------------------------------------------------------------------- -- Test wether the user can delete this object function baseClass.isDeletable(this) if this.Deletable and this.Deletable == 0 then return false end return true end --------------------------------------------------------------------------------------------------------- -- called when the instance is selected (default is no op) function baseClass.onSelect(this) end --------------------------------------------------------------------------------------------------------- -- Tell if object can be selected as next object if a predecessor object -- has been selected in the parent list function baseClass.isNextSelectable(this) return false end --------------------------------------------------------------------------------------------------------- -- get next selectable object (or nil else) function baseClass.getNextSelectableObject(this) local startIndex = this.IndexInParent if type(startIndex) ~= "number" then return nil end local currIndex = startIndex while true do currIndex = currIndex + 1 if currIndex == this.Parent.Size then currIndex = 0 end local instance = this.Parent[currIndex] if currIndex == startIndex then break end if instance ~= nil then local firstSon = instance:getFirstSelectableSon() if firstSon ~= nil and firstSon:isNextSelectable() then return firstSon elseif instance.Selectable and instance:isNextSelectable() then return instance end end end if this.ParentInstance:isKindOf("DefaultFeature") then return this.ParentInstance:getNextSelectableObject() end return nil end --------------------------------------------------------------------------------------------------------- -- select object next to this one, if there's one function baseClass.selectNext(this) local nextSelection = this while 1 do nextSelection = nextSelection:getNextSelectableObject() if not nextSelection or nextSelection == this then r2:setSelectedInstanceId("") return end if nextSelection then -- should not be frozen or hiden if (not nextSelection.DisplayerVisual) or (nextSelection.DisplayerVisual.DisplayMode ~= 1 and nextSelection.DisplayerVisual.DisplayMode ~= 2) then r2:setSelectedInstanceId(nextSelection.InstanceId) return end end end end --------------------------------------------------------------------------------------------------------- -- if an object is not selectable if may nevertheless contain selectable object, the first one is returned by this method function baseClass.getFirstSelectableSon(this) return nil end --------------------------------------------------------------------------------------------------------- -- get select bar type function baseClass.getSelectBarType(this) return r2:evalProp(this:getClass().SelectBarType, this, "") end --------------------------------------------------------------------------------------------------------- -- get name of tree icon function baseClass.getTreeIcon(this) return r2:evalProp(this:getClass().TreeIcon, this, "") end --------------------------------------------------------------------------------------------------------- -- get name of tree icon function baseClass.getPermanentTreeIcon(this) return r2:evalProp(this:getClass().PermanentTreeIcon, this, "") end --------------------------------------------------------------------------------------------------------- -- get name of tree icon according to permanent or current act function baseClass.getContextualTreeIcon(this) if this:getParentAct() and this:getParentAct():isBaseAct() then return this:getPermanentTreeIcon() else return this:getTreeIcon() end end --------------------------------------------------------------------------------------------------------- -- get name of permanent statut icon function baseClass.getPermanentStatutIcon(this) return "" end --------------------------------------------------------------------------------------------------------- -- get name of icon to be displayed in the slect bar function baseClass.getSelectBarIcon(this) return this:getContextualTreeIcon() end --------------------------------------------------------------------------------------------------------- -- Get the display name (in i18n format). This name will be displayed in the property sheet or inthe instance tree function baseClass.getDisplayName(this) local displayName = ucstring() if this.Name ~= nil and this.Name ~= "" then displayName:fromUtf8(this.Name) else return i18n.get("uiR2EDNoName") -- local className = this.Class -- -- tmp patch -- if this:isKindOf("Npc") then -- if this:isBotObject() then -- className = "Bot object" -- end -- end -- className = className .. " : " .. this.InstanceId -- displayName:fromUtf8(className) end return displayName end --------------------------------------------------------------------------------------------------------- -- Get the base name for instance name generation (should return a ucstring) function baseClass.getBaseName(this) return ucstring("") end --------------------------------------------------------------------------------------------------------- -- return true if this instance can by displayed as a button in the select bar function baseClass.displayInSelectBar(this) return true end --------------------------------------------------------------------------------------------------------- -- get first parent that is selectable in the select bar function baseClass.getFirstSelectBarParent(this) local curr = this.ParentInstance while curr and not curr:displayInSelectBar() do curr = curr.ParentInstance end return curr end --------------------------------------------------------------------------------------------------------- -- search the first son that could be inserted in the select bar -- default is to look recursively in the 'son select bar container' function baseClass.getFirstSelectBarSon(this) local sons = this:getSelectBarSons() if not sons then return nil end for k, v in specPairs(sons) do if v:displayInSelectBar() then return v end local firstSelectBarSon = v:getFirstSelectBarSon() if firstSelectBarSon ~= nil then return firstSelectBarSon end end end --------------------------------------------------------------------------------------------------------- -- test if object can have sons than are displayable in the select bar function baseClass.canHaveSelectBarSons(this) return false; end --------------------------------------------------------------------------------------------------------- -- return the default container that may contain object displayable in the select bar function baseClass.getSelectBarSons() return nil end --------------------------------------------------------------------------------------------------------- -- called by the select bar when it displays its menu. Additionnal can be added there function baseClass.completeSelectBarMenu(rootMenu) -- no-op end --------------------------------------------------------------------------------------------------------- -- The following method is called when the default ui displayer wants to know where to attach an object in the instance tree -- Default behaviour is to return the tree node of the parent object when one is found function baseClass.getParentTreeNode(this) parent = this.ParentInstance while parent ~= nil do if parent.User.TreeNodes ~= nil then return parent.User.TreeNodes end parent = parent.ParentInstance end return nil end -------------------------------------------------------------------------------------------- -- Helper function for features : return the feature parent tree node in their act function baseClass.getFeatureParentTreeNode(this) --return this:getParentAct():getContentTreeNodes("macro_components") return this:getParentAct():getContentTreeNodes() end -------------------------------------------------------------------------------------------- -- TODO: test if the object can be exported (true by default) function baseClass.isExportable(this) return true end --------------------------------------------------------------------------------------------------------- -- This method is called by the C++ when the contextual menu is about to be displayed function baseClass.onSetupMenu(this) local class = r2:getClass(this) local menuName = class.Menu if menuName == nil then return end local menu = getUI(menuName) -- setup menu entries to select parents --for i = 1,8 do -- menu["selectParent" .. tostring(i)].active = false --end -- local parent = this.ParentInstance -- for i = 1,9 do -- if parent == nil or parent.Parent == nil then break end -- menu["selectParent" .. tostring(i)].active = true -- menu["selectParent" .. tostring(i)].uc_hardtext = i18n.get("uimR2EDSelectParent") + (parent.InstanceId .. " (" .. parent.Class .. ")") -- --debugInfo(colorTag(0, 255, 255) .. tostring(i)) -- parent = parent.ParentInstance -- end -- -- setup cut & paste entries -- local cuttedSelection = r2:getCuttedSelection() -- if cuttedSelection and this.accept ~= nil then -- local canPaste = this:accept(cuttedSelection) -- debugInfo("canPaste = " .. tostring(canPaste)) -- menu.paste.grayed = not canPaste -- else -- menu.paste.grayed = true -- end -- debug options local extDebug = config.R2EDExtendedDebug == 1 menu.dump_lua_table.active = extDebug menu.inspect_lua_table.active = extDebug menu.translateFeatures.active = extDebug menu.dump_dialogs_as_text.active = extDebug menu.update_dialogs_from_text.active = extDebug menu.cut.active = extDebug menu.paste.active = extDebug r2.ContextualCommands:setupMenu(this, menu) -- delete entries for dynamic content -- local menuRoot = menu:getRootMenu() -- local startLine = menuRoot:getLineFromId("dynamic_content_start") -- local endLine = menuRoot:getLineFromId("dynamic_content_end") -- assert(startLine ~= -1 and endLine ~= -1) -- for lineToDel = endLine - 1, startLine + 1, -1 do -- menuRoot:removeLine(lineToDel) -- end -- retrieve dynamic commands -- local commands = this:getAvailableCommands() -- local currentLine = startLine + 1 -- local commandIndex = 1 -- for commandIndex, command in commands do -- menuRoot:addLineAtIndex(currentLine, i18n.get(command.TextId), "lua", "", "dyn_command_" .. tostring(commandIndex)) -- if there's a bitmap, build a group with the buitmap in it, and add to menu -- if command.ButtonBitmap and command.ButtonBitmap ~= "" then -- local menuButton = createGroupInstance("r2_menu_button", "", { bitmap = command.ButtonBitmap, }) -- if menuButton then -- menuRoot:setUserGroupLeft(currentLine, menuButton) -- end -- end --currentLine = currentLine + 1 --end end --------------------------------------------------------------------------------------------------------- -- Show the property window for this instance -- (react to the event 'show properties' triggered in the ui, by contextual menu or toolbar) function baseClass.onShowProperties(this) -- for now a global (see r2_ui_property_sheet.lua) r2:showProperties(this) end --------------------------------------------------------------------------------------------------------- -- Return list of currently available commands to launch on that instance. -- such commands are displayed in the contextual toolbar or in the contextual menu. -- Returned value should be an array (starting at index 1) with commands of the following format : -- -- { DoCommand = function(instance) ..., -- code to execute when the command is triggered (by menu or toolbar) -- -- Because the function takes 'instance' as a parameter, it may be -- -- either a global function or a method of this class -- Id = "", -- Id of the action. The action "r2ed_context_command" defined in actions.xml -- -- will search for this id when a key is pressed to find the good action -- TextId = "...", -- Text id for entry menu & toolbar tooltip -- ButtonBitmap = "filename.tga", -- Name of the button to display in the toolbar, nil -- -- or "" if the command should not appear in the contextual toolbar -- Separator = "true", -- optionnal, false by default : specify if there should be a separator -- -- between this button and previous buttons -- ShowInMenu = false, -- false if the entry shouldn't be displayed in the menu -- IsActivity = false -- true if event is an activity -- } -- -- 'getAvailableCommands' should be called by derived class, possibly adding their -- own commands -- -- See also : 'buildCommand' function baseClass.getAvailableCommands(this, dest) if this.ParentInstance:isKindOf("UserComponentHolder") then table.insert(dest, this:buildCommand(this.onRemoveFromUserComponent, "removeFromUserComponent", "uimR2EDRemoveFromUserComponent", "")) end if this:isDeletable() and this.User.DeleteInProgress ~= true then table.insert(dest, this:buildCommand(this.onDelete, "delete", "uimR2EDMenuDelete", "r2_toolbar_delete.tga")) end if this:getClass().BuildPropertySheet then table.insert(dest, this:buildCommand(this.onShowProperties, "properties", "uimR2EDMenuProperties", "r2_toolbar_properties.tga", true)) end if this:isKindOf("NpcCustom") then table.insert(dest, this:buildCommand(this.customizeLook, "customize_look", "uiR2EDCustomizeLook", "r2_toolbar_customize_look.tga", false)) end end --------------------------------------------------------------------------------------------------------- -- Build a single command entry to be used by 'getAvailableCommands' -- A command entry translates into a button in the contextual toolbar function baseClass.buildCommand(this, command, id, textId, bitmap, separator, showInMenu) if showInMenu == nil then showInMenu = true end return { DoCommand = command, TextId = textId, Id = id, ButtonBitmap = bitmap, Separator = separator, ShowInMenu = showInMenu, IsActivity = false } end --------------------------------------------------------------------------------------------------------- -- same as 'buildCommand', but for activities function baseClass.buildActivityCommand(this, command, id, textId, bitmap, separator, showInMenu) local result = this:buildCommand(command, id, textId, bitmap, separator, showInMenu) result.IsActivity = true return result end --------------------------------------------------------------------------------------------------------- -- Special, class method (not instance method) for dev : returns a table containing all infos on which the xml generic property sheet depends -- When this table is modified, then the xml property sheet will be rebuild for this class (when 'resetEditor' -- is called for example. function baseClass.ClassMethods.getGenericPropertySheetCacheInfos(this) local infos = {} infos.Prop = this.Prop -- if one of the properties change, then must rebuild the property sheet infos.PropertySheetHeader = this.PropertySheetHeader -- if the xml header change, then must rebuild the sheet, too return infos end --------------------------------------------------------------------------------------------------------- -- get list of command for display in the mini toolbar function baseClass.getAvailableMiniCommands(this, result) -- OBSOLETE -- table.insert(result, this:buildCommand(this.editDialogs, "edit_dialogs", "uiR2EDEditDialogs", "r2_icon_dialog_mini.tga")) -- table.insert(result, this:buildCommand(this.editActions, "edit_actions", "uiR2EDEditActions", "r2_icon_action_mini.tga")) -- table.insert(result, this:buildCommand(this.editReactions, "edit_reactions", "uiR2EDEditReactions", "r2_icon_reaction_mini.tga")) end --------------------------------------------------------------------------------------------------------- -- Return true if sequences can be edited on that object function baseClass.isSequencable(this) return false end --------------------------------------------------------------------------------------------------------- -- For sequencable object only (baseClass.isSequencable) : return the lookup string for a verb from an activity name -- Indeed, an activity may have different name depending on who performs it -- for example, the "Feed In Zone" activity will be name "work" for a worker kitin instead of "feed" for a carnivore function baseClass.getActivityVerbLookupName(this, activityName) return activityName end --------------------------------------------------------------------------------------------------------- -- is the object global to the scenario ? The select bar will call this to force the good update -- for global objects that are selectable (plot items ...) function baseClass.isGlobalObject(this) return false end function baseClass.onRemoveFromUserComponent(this) r2_core.CurrentHolderId = this.ParentInstance.InstanceId r2_core:removeUserComponentElement(this.InstanceId) end ------------- -- REF IDS -- ------------- -- Set the value of a refId inside this object. (empty string to delete) -- This will push a new action name & call r2.requestNode function baseClass.setRefIdValue(this, refIdName, targetId) local name = this:getDisplayName() local refIdUCName = r2:getPropertyTranslation(this:getClass().NameToProp[refIdName]) if targetId == "" then r2.requestNewAction(concatUCString(i18n.get("uiR2EDRemovingTargetAction"), name, i18n.get("uiR2EDAddingReferenceSeparator"), refIdname)) else local targetName = r2:getInstanceFromId(targetId):getDisplayName() r2.requestNewAction(concatUCString(i18n.get("uiR2EDAddingReferenceAction"), name, i18n.get("uiR2EDAddingReferenceSeparator"), refIdUCName, i18n.get("uiR2EDAddingReferenceToAction"), targetName)) end r2.requestSetNode(this.InstanceId, refIdName, targetId) end --------------------------- -- COPY / PASTE HANDLING -- --------------------------- --------------------------------------------------------------------------------------------------------- -- see if this object support copy (false by default) function baseClass.isCopyable(this) return false end --------------------------------------------------------------------------------------------------------- -- Create a canonical copy of this object, this copy can be used by subsequent calls to 'newCopy' function baseClass.copy(this) -- implementation in "r2_base_class_private.lua" end -- Create a new copy from a canonical copy -- New instance ids are generated -- Default behavior is to remove all external dependencies and to rename -- internal dependencies -- The result can be used as input to 'paste' & 'ghostPaste' function baseClass.newCopy(canonicalCopy) -- implementation in "r2_base_class_private.lua" end --------------------------------------------------------------------------------------------------------- -- Paste the current clipboard -- not really a method here, because 'src' id a lua table (should be the content of the clipboard ...) that can be used with -- a r2.request.. command. -- - this function should copy the result in a suitable place (maybe into current selection, or at global scope) -- NB : if newPlace is not true, then the result should be past at the coordinates found in src -- - It should check that there is some room in the scenario before doing the copy function baseClass.paste(src, newPlace, srcInstanceId) if r2:getLeftQuota() <= 0 then r2:makeRoomMsg() return end end function baseClass.pasteGhost(src) if r2:getLeftQuota() <= 0 then r2:makeRoomMsg() return end end -- TMP TMP : move events test -- function baseClass.onTargetInstancePreHrcMove(this, targetAttr, targetIndexInArray) debugInfo(string.format("instance: pre hrc move : %s", targetAttr)) end -- function baseClass.onTargetInstancePostHrcMove(this, targetAttr, targetIndexInArray) debugInfo(string.format("instance : post hrc move : %s", targetAttr)) end -- IMPLEMENTATION r2.doFile("r2_base_class_private.lua") r2.registerComponent(baseClass) baseClass = nil