Image
-- Version History:
--==================
-- Version 2.1 (by Lukas Krepel, Frame Order)
	-- Updated for future Moho versions -- *** TODO/WIP ***
-- Version 2.0 (by Lukas Krepel, Frame Order)
	-- Revamped most of the script, consolidated a lot in FO_Channels utility.
-- Version 1.3.4 (by Lukas Krepel, Frame Order)
	-- Removed Reset button, because Moho already has a reset function for tools -- Tick!
-- Version 1.3.3 (by Lukas Krepel, Frame Order)
	-- Simplified description -- Tick!
-- Version 1.3.2 (by Lukas Krepel, Frame Order)
	-- Moho 13.5 support -- Tick!
	-- Vitruvion bones channels -- Tick!
	-- Added Wind channels -- Tick!
	-- Added Wind Checkbox -- Tick!
	-- Modified shortcuts to match other tools -- Tick!
	-- Fixed some toolbar icons -- Tick!
	-- Support for userscripts MohoMode.lua and LK_PaintKeys.lua (not required) -- Tick!
-- Version 1.3.1 (by Lukas Krepel, Frame Order)
	-- Support for channels split dimensions (not for individual bones) (thanks to synthsyn75) -- Tick!
-- Version 1.3 (by Lukas Krepel, Frame Order)
	-- Added all channels up to Moho 12	-- Tick!
	-- New icons that support the new color schemes and higher resolutions -- Tick!
	-- Reformatted the toolbar layout to be more user friendly -- Tick !
	-- Added shortcuts to tool description -- Tick!
	-- Disable 'selected points/bones/shapes' buttons if 'points/bones/shapes' buttons are off. -- Tick!
	-- Hopefully I haven't destroyed any functionality... -- Tick!
-- Version 1.2
	-- Fixed for AS version 7	-- Tick!
	-- Used built-in layer visibilty check instead of hack	-- Tick!
	-- Used layer:Mesh() instead of having to activate each layer	-- Tick!
	-- Allowed keys to be deleted using shift count without shifting	-- Tick!
	-- Restricted shifting to above frame 1 instead of frame 0	-- Tick!
	-- Include negative timeline layer keys in consolidated timeline -- Tick!
	-- Added support for specifying a list of actions to nudge as well as main timeline	-- Tick!
	-- Fixed bug where detected keys in one channel may overrided detected keys in another	-- Tick!
	-- Fixed bug where not all keys of sequencer-shifted layer would be displayed in consolidated timeline	-- Tick!
-- Version 1.0
	-- Moved rt_utilities functions to their own table instead of using MOHO	 -- Tick!
	-- Created this script to roll all of the nudge tools into one -- Tick!
	-- Now preserving key parameters for interpolation modes that have them (applies to ver 6.0 only)	-- Tick!
	-- Now taking into account timing offset from sequencer (applies to ver 6.0 only)	-- Tick!
	-- Added some missing animation channels:
	-- BoneDynamics channel -- Tick!
	-- Effect transform channels (ver 6.0 only) -- Tick!
	-- Shadow and shading noise channels (ver 6.0 only) -- Tick!
	-- Audio layer channels (ver 6.1 only) -- Tick!
	-- Shape effect channel (Still not available in ver 6.1)
	-- Point curvature channel (ver 6.1 only) -- Tick!
	-- Added options for omitting certain channels	-- Tick!
	-- Added options for only affecting selected objects	-- Tick!
	-- Added option for only affecting selected layers (ver 6.0 only)
	-- NB Selected layers only updated when rt_consolidated_layers is run
	-- Added option for only affecting visible layers -- Tick! 
	-- Added option to specify nudge count -- Tick!
	-- Added option to only nudge keys on current frame -- Tick!
	-- Added option to enable or disable camera channels -- Tick!
	-- Added option to not move cursor -- Tick!
-- Version 0.1 
	-- Initial Release

-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************

ScriptName = "LK_NudgeKeys"

-- **************************************************
-- General information about this script
-- **************************************************

LK_NudgeKeys = {}

function LK_NudgeKeys:ColorizeIcon()
	return true
end

function LK_NudgeKeys:Name()
	return "Nudge Keys Toolset"
end

function LK_NudgeKeys:Version()
	return "2.0"
end

-- * Map shortcut keys to keyboard keys
LK_NudgeKeys.shortcutPrevKey							= ','
LK_NudgeKeys.shortcutNextKey							= '.'
LK_NudgeKeys.shortcutNudgeKeysLeft						= '-'
LK_NudgeKeys.shortcutNudgeKeysRight						= '='
LK_NudgeKeys.shortcutGlobalNudgeKeysLeft				= '<'
LK_NudgeKeys.shortcutGlobalNudgeKeysRight 				= '>'
LK_NudgeKeys.ShortcutConsolidatedTimeline 				= 'i'

LK_NudgeKeys.onlyNudgeDocumentChannelsOnGlobalNudges 	= true

function LK_NudgeKeys:Description()
	return "Tool for interactively manipulating keys. (<"..self.shortcutPrevKey.."/"..self.shortcutNextKey.."> Go to previous/next keyframe, <"..self.shortcutNudgeKeysLeft.."/"..self.shortcutNudgeKeysRight.."> Nudge keys left/right, <"..self.shortcutGlobalNudgeKeysLeft.."/"..self.shortcutGlobalNudgeKeysRight.."> Global nudge keys left/right, <"..self.ShortcutConsolidatedTimeline.."> Create consolidated timeline markers)."
end

function LK_NudgeKeys:Creator()
	return "Lukas Krepel, Frame Order (Original tool by Rudiger)"
end

function LK_NudgeKeys:UILabel()
	return "Nudge Keys Toolset"
end

function LK_NudgeKeys:IsRelevant()
	if MohoMode ~= nil then
		if not (MohoMode.keys or MohoMode.raymond) then
			return false
		end
	end
	return true
end

function LK_NudgeKeys:LoadPrefs(prefs)
	self.onlyNudgeDocumentChannelsOnGlobalNudges = prefs:GetBool("LK_NudgeKeys.onlyNudgeDocumentChannelsOnGlobalNudges", true)
	-- * Channels:
	FO_Channels:LoadPrefs(prefs)
	-- * TODO TODO TODO CLEAN THIS UP
	self.allow_key_deletion = prefs:GetBool("LK_NudgeKeys.allow_key_deletion", false)
	self.overwrite_mode = prefs:GetBool("LK_NudgeKeys.overwrite_mode", false)
	self.nudge_count = prefs:GetInt("LK_NudgeKeys.nudge_count", 1)
	self.current_frame_only = prefs:GetBool("LK_NudgeKeys.current_frame_only", false)
	self.global_move_cursor = prefs:GetBool("LK_NudgeKeys.global_move_cursor", true)
	self.move_cursor = prefs:GetBool("LK_NudgeKeys.move_cursor", true)
	self.global_move_endFrame = prefs:GetBool("LK_NudgeKeys.global_move_endFrame", true)
	self.move_endFrame = prefs:GetBool("LK_NudgeKeys.move_endFrame", false)
end

function LK_NudgeKeys:SavePrefs(prefs)
	prefs:SetBool("LK_NudgeKeys.onlyNudgeDocumentChannelsOnGlobalNudges", self.onlyNudgeDocumentChannelsOnGlobalNudges)
	-- * Channels:
	FO_Channels:SavePrefs(prefs)
	-- * TODO TODO TODO CLEAN UP
	prefs:SetBool("LK_NudgeKeys.allow_key_deletion", self.allow_key_deletion)
	prefs:SetBool("LK_NudgeKeys.overwrite_mode", self.overwrite_mode)
	prefs:SetInt("LK_NudgeKeys.nudge_count", self.nudge_count) 
	prefs:SetBool("LK_NudgeKeys.current_frame_only", self.current_frame_only)
	prefs:SetBool("LK_NudgeKeys.global_move_cursor", self.global_move_cursor)
	prefs:SetBool("LK_NudgeKeys.move_cursor", self.move_cursor)
	prefs:SetBool("LK_NudgeKeys.global_move_endFrame", self.global_move_endFrame)
	prefs:SetBool("LK_NudgeKeys.move_endFrame", self.move_endFrame)
end

function LK_NudgeKeys:ResetPrefs()
	LK_NudgeKeys.onlyNudgeDocumentChannelsOnGlobalNudges = true
	-- * Channels:
	FO_Channels:ResetPrefs()
	-- *
	self.allow_key_deletion = false -- * Set to allow existing keys to be deleted with backspace
	self.overwrite_mode = false -- * Set option for overwriting frames
	self.nudge_count = 1
	self.current_frame_only = false
	self.global_move_cursor = true
	self.move_cursor = true
	self.global_move_endFrame = true
	self.move_endFrame = false
end

LK_NudgeKeys.MARK_CONSOLIDATED_TIMELINE_GLOBAL		= MOHO.MSG_BASE
LK_NudgeKeys.MARK_CONSOLIDATED_TIMELINE 			= MOHO.MSG_BASE + 21--*
LK_NudgeKeys.CLEAR_CONSOLIDATED_TIMELINE_GLOBAL		= MOHO.MSG_BASE + 22--*
LK_NudgeKeys.CLEAR_CONSOLIDATED_TIMELINE 			= MOHO.MSG_BASE + 1
LK_NudgeKeys.PREV_KEY_FRAME 						= MOHO.MSG_BASE + 2
LK_NudgeKeys.NEXT_KEY_FRAME 						= MOHO.MSG_BASE + 3
LK_NudgeKeys.NUDGE_KEYS_LEFT 						= MOHO.MSG_BASE + 4
LK_NudgeKeys.NUDGE_KEYS_RIGHT 						= MOHO.MSG_BASE + 5
LK_NudgeKeys.GLOBAL_NUDGE_KEYS_LEFT 				= MOHO.MSG_BASE + 6
LK_NudgeKeys.GLOBAL_NUDGE_KEYS_RIGHT 				= MOHO.MSG_BASE + 7
LK_NudgeKeys.TOGGLE_OVERWRITE_MODE					= MOHO.MSG_BASE + 8
LK_NudgeKeys.NUDGE_COUNT 							= MOHO.MSG_BASE + 9
LK_NudgeKeys.TOGGLE_CURRENT_FRAME_ONLY 				= MOHO.MSG_BASE + 10
LK_NudgeKeys.UPDATE_FLAGS 							= MOHO.MSG_BASE + 11
LK_NudgeKeys.ALLOW_KEYDELETION 						= MOHO.MSG_BASE + 12
LK_NudgeKeys.TOGGLE_MOVE_CURSOR 					= MOHO.MSG_BASE + 13
LK_NudgeKeys.TOGGLE_GLOBAL_MOVE_CURSOR 				= MOHO.MSG_BASE + 14
LK_NudgeKeys.TOGGLE_MOVE_ENDFRAME 					= MOHO.MSG_BASE + 15
LK_NudgeKeys.TOGGLE_GLOBAL_MOVE_ENDFRAME 			= MOHO.MSG_BASE + 16
LK_NudgeKeys.FILTER_LAYER_EXPRESSION				= MOHO.MSG_BASE + 17
LK_NudgeKeys.ACTIONS_LIST 							= MOHO.MSG_BASE + 18
LK_NudgeKeys.INTERP_MODE 							= MOHO.MSG_BASE + 19
LK_NudgeKeys.TOGGLE_GLOBAL 							= MOHO.MSG_BASE + 20

LK_NudgeKeys.TOGGLE_CHANNELS 						= MOHO.MSG_BASE + 100 -- * 100 t/m 115

-- **************************************************
-- KeyTool Advanced Options Pop-up Dialog
-- **************************************************

local LK_NudgeKeysDialog = {}

function LK_NudgeKeysDialog:new()
	local d = LM.GUI.SimpleDialog("KeyTool Advanced Options", LK_NudgeKeysDialog)
	local l = d:GetLayout()
	-- *****************
	-- *** Channels: ***
	-- *****************
	FO_Utilities:DialogDivider(l, "Channels", true)
	-- *
	d.onlyNudgeDocumentChannelsOnGlobalNudgesCheckbox = LM.GUI.CheckBox("Only nudge Document-Channels on Global-Nudges", LK_NudgeKeys.TOGGLE_GLOBAL)
	l:AddChild(d.onlyNudgeDocumentChannelsOnGlobalNudgesCheckbox, LM.GUI.ALIGN_LEFT)
	-- *****************
	-- *** Keys: ***
	-- *****************
	FO_Utilities:DialogDivider(l, "Keys", false)
	-- *
	d.overwriteModeCheckbox = LM.GUI.CheckBox("Allow Key Overwriting", LK_NudgeKeys.TOGGLE_OVERWRITE_MODE)
	l:AddChild(d.overwriteModeCheckbox, LM.GUI.ALIGN_LEFT)
	-- *
	d.allowKeyDeletionCheckbox = LM.GUI.CheckBox("Allow Key Deletion", LK_NudgeKeys.ALLOW_KEYDELETION)
	l:AddChild(d.allowKeyDeletionCheckbox, LM.GUI.ALIGN_LEFT)
	-- *****************
	-- *** Include: ***
	-- *****************
	FO_Utilities:DialogDivider(l, "Include", false)
	-- *
	l:PushH()
		l:PushV()
			l:AddChild(LM.GUI.StaticText("Timeline-Cursor:"), LM.GUI.ALIGN_LEFT)
			l:AddChild(LM.GUI.StaticText("Endframe:"), LM.GUI.ALIGN_LEFT)
		l:Pop()
		l:PushV()
			d.globalMoveCursorCheckbox = LM.GUI.CheckBox("Global", LK_NudgeKeys.TOGGLE_GLOBAL_MOVE_CURSOR)
			l:AddChild(d.globalMoveCursorCheckbox, LM.GUI.ALIGN_LEFT)
			d.globalMoveEndframeCheckbox = LM.GUI.CheckBox("Global", LK_NudgeKeys.TOGGLE_GLOBAL_MOVE_ENDFRAME)
			l:AddChild(d.globalMoveEndframeCheckbox, LM.GUI.ALIGN_LEFT)
		l:Pop()
		l:PushV()
			d.moveCursorCheckbox = LM.GUI.CheckBox("Normal", LK_NudgeKeys.TOGGLE_MOVE_CURSOR)
			l:AddChild(d.moveCursorCheckbox, LM.GUI.ALIGN_LEFT)
			d.moveEndframeCheckbox = LM.GUI.CheckBox("Normal", LK_NudgeKeys.TOGGLE_MOVE_ENDFRAME)
			l:AddChild(d.moveEndframeCheckbox, LM.GUI.ALIGN_LEFT)
		l:Pop()
	l:Pop()
	return d
end

function LK_NudgeKeysDialog:UpdateWidgets()
	self.onlyNudgeDocumentChannelsOnGlobalNudgesCheckbox:SetValue(LK_NudgeKeys.onlyNudgeDocumentChannelsOnGlobalNudges)
	self.overwriteModeCheckbox:SetValue(LK_NudgeKeys.overwrite_mode)
	self.allowKeyDeletionCheckbox:SetValue(LK_NudgeKeys.allow_key_deletion)
	self.globalMoveCursorCheckbox:SetValue(LK_NudgeKeys.global_move_cursor)
	self.moveCursorCheckbox:SetValue(LK_NudgeKeys.move_cursor)
	self.globalMoveEndframeCheckbox:SetValue(LK_NudgeKeys.global_move_endFrame)
	self.moveEndframeCheckbox:SetValue(LK_NudgeKeys.move_endFrame)
end

function LK_NudgeKeysDialog:OnValidate()
	-- *
end

function LK_NudgeKeysDialog:OnOK()
	LK_NudgeKeys.onlyNudgeDocumentChannelsOnGlobalNudges = self.onlyNudgeDocumentChannelsOnGlobalNudgesCheckbox:Value()
	LK_NudgeKeys.overwrite_mode = self.overwriteModeCheckbox:Value()
	LK_NudgeKeys.allow_key_deletion = self.allowKeyDeletionCheckbox:Value()
	LK_NudgeKeys.global_move_cursor = self.globalMoveCursorCheckbox:Value()
	LK_NudgeKeys.move_cursor = self.moveCursorCheckbox:Value()
	LK_NudgeKeys.global_move_endFrame = self.globalMoveEndframeCheckbox:Value()
	LK_NudgeKeys.move_endFrame = self.moveEndframeCheckbox:Value()		
end

function LK_NudgeKeysDialog:HandleMessage(msg)
	-- *
end

-- ****************************************************************************
function LK_NudgeKeys:OnMouseDown(moho, mouseEvent)
	 mouseEvent.view:DrawMe()
end

function LK_NudgeKeys:DrawMe(moho, view)
	-- FO_Utilities:DrawMeTinyUI(moho)
end

function LK_NudgeKeys:OnKeyDown(moho, keyEvent)
	-- * Shortcut Key Mapping:
	if keyEvent.key == self.shortcutPrevKey then
		LK_NudgeKeys:HandleMessage(moho, view, self.PREV_KEY_FRAME)
	elseif keyEvent.key == self.shortcutNextKey then
		LK_NudgeKeys:HandleMessage(moho, view, self.NEXT_KEY_FRAME)
	elseif keyEvent.key == self.shortcutNudgeKeysLeft then
		LK_NudgeKeys:HandleMessage(moho, view, self.NUDGE_KEYS_LEFT)
	elseif keyEvent.key == self.shortcutNudgeKeysRight then
		LK_NudgeKeys:HandleMessage(moho, view, self.NUDGE_KEYS_RIGHT)
	elseif keyEvent.key == self.shortcutGlobalNudgeKeysLeft then
		LK_NudgeKeys:HandleMessage(moho, view, self.GLOBAL_NUDGE_KEYS_LEFT)
	elseif keyEvent.key == self.shortcutGlobalNudgeKeysRight then
		LK_NudgeKeys:HandleMessage(moho, view, self.GLOBAL_NUDGE_KEYS_RIGHT)
	elseif keyEvent.key == self.ShortcutConsolidatedTimeline then
		LK_NudgeKeys:HandleMessage(moho, view, self.MARK_CONSOLIDATED_TIMELINE_GLOBAL)
	end
end

function LK_NudgeKeys:DoLayout(moho, layout)
	FO_Utilities.tinyUI = false
	-- * Consolidate Button:
	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.createConsolidatedTimelineButton = LM.GUI.Button("Mark Timeline", self.MARK_CONSOLIDATED_TIMELINE_GLOBAL)
	self.createConsolidatedTimelineButton:SetAlternateMessage(self.MARK_CONSOLIDATED_TIMELINE)
	self.createConsolidatedTimelineButton:SetToolTip("Create Consolidated Timeline as Document-wide Markers (" .. LK_NudgeKeys.ShortcutConsolidatedTimeline .. ") (Hold <alt> for local layer only)")
	layout:AddChild(self.createConsolidatedTimelineButton)
	self.removeConsolidatedTimelineButton = LM.GUI.ImageButton("ScriptResources/FO_icons/trashcan", "Clear Consolidated Timeline (Hold <alt> for local layer only)", false, self.CLEAR_CONSOLIDATED_TIMELINE_GLOBAL, true)
	self.removeConsolidatedTimelineButton:SetAlternateMessage(self.CLEAR_CONSOLIDATED_TIMELINE)
	layout:AddChild(self.removeConsolidatedTimelineButton)
	layout:Pop()
	-- *****************	
	-- *** Navigate: ***
	-- *****************
	FO_Utilities:Divider(layout, "Navigate")
	layout:PushH(LM.GUI.ALIGN_CENTER, 2)
	-- * Prev key button:
	self.prevKeyFrameButton = LM.GUI.ImageButton("ScriptResources/FO_icons/go_to_previous_keyframe", "Prev Keyframe ("..self.shortcutPrevKey..")", false, self.PREV_KEY_FRAME, true)
	layout:AddChild(self.prevKeyFrameButton)
	-- * Next key button:
	self.nextKeyFrameButton = LM.GUI.ImageButton("ScriptResources/FO_icons/go_to_next_keyframe", "Next Keyframe ("..self.shortcutNextKey..")", false, self.NEXT_KEY_FRAME, true)
	layout:AddChild(self.nextKeyFrameButton)
	layout:Pop()
	-- **************	
	-- *** Nudge: ***
	-- **************
	FO_Utilities:Divider(layout, "Nudge")
	layout:PushH(LM.GUI.ALIGN_CENTER, 2)
	-- * Nudge Left:
	self.nudgeKeysLeftButton = LM.GUI.ImageButton("ScriptResources/FO_icons/nudge_keys_left_button", "Nudge Keys Left ("..self.shortcutNudgeKeysLeft..") (Nudges current layer)", false, self.NUDGE_KEYS_LEFT, true)
	layout:AddChild(self.nudgeKeysLeftButton)
	-- * Nudge Right:
	self.nudgeKeysRightButton = LM.GUI.ImageButton("ScriptResources/FO_icons/nudge_keys_right_button", "Nudge Keys Right ("..self.shortcutNudgeKeysRight..") (Nudges current layer)", false, self.NUDGE_KEYS_RIGHT, true)
	layout:AddChild(self.nudgeKeysRightButton)
	-- * Global Nudge Left:
	self.globalNudgeKeysLeftButton = LM.GUI.ImageButton("ScriptResources/FO_icons/nudge_global_keys_left_button", "Global Nudge Keys Left ("..self.shortcutGlobalNudgeKeysLeft..") (Nudges all root layers)", false, self.GLOBAL_NUDGE_KEYS_LEFT, true)
	layout:AddChild(self.globalNudgeKeysLeftButton)
	-- * Global Nudge Right:
	self.globalNudgeKeysRightButton = LM.GUI.ImageButton("ScriptResources/FO_icons/nudge_global_keys_right_button", "Global Nudge Keys Right ("..self.shortcutGlobalNudgeKeysRight..") (Nudges all root layers)", false, self.GLOBAL_NUDGE_KEYS_RIGHT, true)
	layout:AddChild(self.globalNudgeKeysRightButton)
	layout:Pop()
	-- ***************	
	-- *** Amount: ***
	-- ***************
	FO_Utilities:Divider(layout, "Amount")
	self.nudgeCountTextbox = LM.GUI.TextControl(0, "000", self.NUDGE_COUNT, LM.GUI.FIELD_INT)
	layout:AddChild(self.nudgeCountTextbox)
	-- * Toggle Current Frame Only Button:
	self.currentFrameOnlyCheckbox = LM.GUI.ImageButton("ScriptResources/FO_icons/nudge_current_frame_only", "Current Frame Only", true, self.TOGGLE_CURRENT_FRAME_ONLY, true)
	layout:AddChild(self.currentFrameOnlyCheckbox)
	-- *****************
	-- *** Channels: ***
	-- *****************
	FO_Channels:DoLayout(moho, layout, self.TOGGLE_CHANNELS)
	-- * Advanced Dropdown:
	self.dlog = LK_NudgeKeysDialog:new()
	self.popup = LM.GUI.PopupDialog("Advanced")
	self.popup:SetDialog(self.dlog)
	layout:AddChild(self.popup)
end

function LK_NudgeKeys:UpdateWidgets(moho)
	-- * Channels:
	FO_Channels:UpdateWidgets(moho)
	-- *
	self.nudgeCountTextbox:SetValue(LK_NudgeKeys.nudge_count)
	self.currentFrameOnlyCheckbox:SetValue(LK_NudgeKeys.current_frame_only)
	--
	self:FilterByMarker(moho)
end 

function LK_NudgeKeys:HandleMessage(moho, view, msg)
	-- * Channels:
	FO_Channels:HandleMessage(moho, view, msg, self.TOGGLE_LAYER_CHANNELS)
	-- *
	if (msg == self.PREV_KEY_FRAME) then
		FO_Channels:GoToPrevNextKeyFrame(moho, true)
	elseif (msg == self.NEXT_KEY_FRAME) then
		FO_Channels:GoToPrevNextKeyFrame(moho, false)
	elseif (msg == self.NUDGE_KEYS_LEFT) then
		local right = false
		local global = false
		LK_NudgeKeys:RunShift(moho, right, global)
	elseif (msg == self.NUDGE_KEYS_RIGHT) then
		local right = true
		local global = false
		LK_NudgeKeys:RunShift(moho, right, global)
	elseif (msg == self.GLOBAL_NUDGE_KEYS_LEFT) then
		local right = false
		local global = true
		LK_NudgeKeys:RunShift(moho, right, global)
	elseif (msg == self.GLOBAL_NUDGE_KEYS_RIGHT) then
		local right = true
		local global = true
		LK_NudgeKeys:RunShift(moho, right, global)
	elseif (msg == self.NUDGE_COUNT) then
		LK_NudgeKeys.nudge_count = self.nudgeCountTextbox:IntValue()
	elseif (msg == self.TOGGLE_CURRENT_FRAME_ONLY) then
		LK_NudgeKeys.current_frame_only = self.currentFrameOnlyCheckbox:Value()
	elseif (msg == self.MARK_CONSOLIDATED_TIMELINE) then
		self:ConsolidateTimelineAsDocMarkers(moho, false)
	elseif (msg == self.MARK_CONSOLIDATED_TIMELINE_GLOBAL) then
		self:ConsolidateTimelineAsDocMarkers(moho, true)
	elseif (msg == self.CLEAR_CONSOLIDATED_TIMELINE) then
		self:ClearConsolidatedTimelineDocMakers(moho, false)
	elseif (msg == self.CLEAR_CONSOLIDATED_TIMELINE_GLOBAL) then
		self:ClearConsolidatedTimelineDocMakers(moho, true)
	end
	-- * Color Keys:
	FO_Utilities:PaintKeys(moho)
	-- * Update User Interface:
	moho:UpdateUI()
end

function LK_NudgeKeys:SetConsolidatedMarkers(moho, layer, global)
	-- * Merge all timelines related to layer and put them on points layer
	-- * Consolidated channel is built up 1 layer at a time to save time
	local consolidatedChannel = moho.document.fTimelineMarkers
	if not global then
		consolidatedChannel = moho.layer.fTimelineMarkers
	end
	
	local timing_offset = 0
	if layer.TotalTimingOffset ~= nil then
		timing_offset = layer:TotalTimingOffset()
	end

	local channels = {}
	FO_Channels:AddLayerChannels(moho, layer, channels)
	for i = 1, #channels do
		local channel = channels[i]
		local duration = channel:Duration()-timing_offset
		-- * Make it start at -timing_offset to speed things up, but means negative layer frames won't show up
		-- * Would really like a layer:StartFrame() method
		for frame = -timing_offset, duration do
			if channel:HasKey(frame+timing_offset) then
				if consolidatedChannel:HasKey(frame) then
					local markerInterp = MOHO.InterpSetting:new_local()
					consolidatedChannel:GetKeyInterp(frame, markerInterp)
					if markerInterp.tags == 7 then -- * 7 = Tan
						local markerVal = consolidatedChannel:GetValue(frame)
						local layerName = layer:Name()
						-- *
						if string.match(layerName, " %(") then
							layerName = string.sub(layerName, 0, string.find(layerName, " %(")-1)
						end
						-- *
						if not string.match(markerVal, layerName) then
							markerVal = markerVal.." | "..layerName
							consolidatedChannel:SetValue(frame, markerVal)
						end
					end
					--
				elseif not consolidatedChannel:HasKey(frame) then
					if frame ~= 0 then
						-- * Add Marker:
						consolidatedChannel:SetValue(frame, layer:Name())
						local markerInterp = MOHO.InterpSetting:new_local()
						markerInterp.tags = 7 -- * 7 = Tan
						consolidatedChannel:SetKeyInterp(frame, markerInterp)
					end
				end
			end
		end
	end
end

function LK_NudgeKeys:ConsolidateTimelineAsDocMarkers(moho, global)
	moho.document:SetDirty()
	moho.document:PrepUndo("", moho.layer, false)
	FO_Channels:BruteOffsetFix(moho)
	self:ClearConsolidatedTimelineDocMakers(moho, global)
	local layers = FO_Channels:PreferredLayers(moho, global)
	-- local layers = {}
	-- -- *
	-- if global then
	-- 	for i = 0, moho.document:CountLayers()-1 do
	-- 		local rootLayer = moho.document:Layer(i)
	-- 		FO_Channels:AddLayerWithChildren(moho, rootLayer, layers)
	-- 	end
	-- else
	-- 	FO_Channels:AddLayerWithChildren(moho, moho.layer, layers)
	-- end
	-- *
	for i = 1, #layers do
		self:SetConsolidatedMarkers(moho, layers[i], global)
	end

	moho:UpdateUI()
	-- * Update current frame
	moho.view:DrawMe()
end

function LK_NudgeKeys:ClearConsolidatedTimelineDocMakers(moho, global)
	moho.document:SetDirty()
	moho.document:PrepUndo("", moho.layer, false) -- * todo begrijpen of dit voldoende is of multi moet? weet ik veel
	-- *
	local consolidatedChannel =	moho.document.fTimelineMarkers
	if not global then
		consolidatedChannel = moho.layer.fTimelineMarkers
	end
	duration = consolidatedChannel:Duration()
	for frame = 0, duration do
		if consolidatedChannel:HasKey(frame) then
			local markerInterp = MOHO.InterpSetting:new_local()
			consolidatedChannel:GetKeyInterp(frame, markerInterp)
			if markerInterp.tags == 7 then -- * 7 = Tan
				consolidatedChannel:DeleteKey(frame)
			end
		end
	end
end

function LK_NudgeKeys:ChannelShift(current_frame, channel, count, forceAll)
	-- * Shift all channel keys after and including current frame by count value:
	local frame
	local val1 = 0
	local val2 = 0
	local duration
	local startFrame
	local endFrame
	local incr
	local right_shift
	if count > 0 then
		right_shift = true
	else
		right_shift = false
	end
	-- * Delete frames within count range of cursor:
	if not right_shift or self.current_frame_only or self.overwrite_mode then
		if right_shift then
			if self.overwrite_mode then
				startFrame = current_frame
			else
				startFrame = current_frame+1
			end
			endFrame = current_frame+count
			incr = 1
		else
			if self.overwrite_mode then
				startFrame = current_frame
			else
				startFrame = current_frame-1
			end
			endFrame = current_frame+count
			incr = -1
		end
		if self.allow_key_deletion then
			for frame = startFrame+incr, endFrame, incr do
				if channel:HasKey(frame) then
					channel:DeleteKey(frame)
				end
			end
		end
	end
	-- *
	if self.current_frame_only and not forceAll then
		duration = current_frame
	else
		duration = channel:Duration()
	end
	-- *
	if right_shift then 
		-- * We need to start at the last keyframe so we don't overwrite keys
		startFrame = duration
		endFrame = current_frame
		incr = -1
	else
		startFrame = current_frame
		endFrame = duration
		incr = 1
	end
	-- * Interface doesn't currently allow keys to be moved
	-- * so have to create new keys and delete the old ones
	for frame = startFrame, endFrame, incr do
		if channel:HasKey(frame) then
			channel:AddKey(frame + count)
			local interp_mode = MOHO.InterpSetting:new_local()
			channel:GetKeyInterp(frame, interp_mode)
			channel:SetValue(frame + count, channel:GetValue(frame))
			channel:SetKeyInterp(frame + count, interp_mode)
			channel:DeleteKey(frame)
		end
	end
end

function LK_NudgeKeys:LayerShift(current_frame, moho, count, layer, forceAll)
	-- * Shift all keys in layer and its sub layers   
	local layers = {}
	FO_Channels:AddLayerWithChildren(moho, layer, layers)
	for i = 1, #layers do
		local layer = layers[i]
		-- * Loop through channels for current layer:
		local channels = {}
		channels = FO_Channels:AddLayerChannels(moho, layer, channels, forceAll)
		for i = 1, #channels do
			self:ChannelShift(current_frame, channels[i], count, forceAll)
		end
	end
end

function LK_NudgeKeys:GlobalLayerShift(current_frame, moho, count)
	-- * Shift all keys for all root layers in document
	for i = 0, moho.document:CountLayers()-1 do
		self:LayerShift(current_frame, moho, count, moho.document:Layer(i))
	end
end

-- **************************************************
-- Nudge!
-- **************************************************
function LK_NudgeKeys:RunShift(moho, right, global)
	-- * Prepundo:
	moho.document:PrepUndo(moho.layer, true)
	moho.document:SetDirty()
	local layer = moho.layer
	local frame = moho.frame
	local actual_nudge_count = self.nudge_count
	local endFrame 
	-- * Don't run on frame 0!
	if frame == 0 then
		return
	end
	-- * Don't nudge past frame 1!
	if not right then
		endFrame = frame - actual_nudge_count
		if endFrame < 1 then
			actual_nudge_count = frame - 1 --why?
		end
	else
		endFrame = frame + actual_nudge_count
	end
	-- *
	if not self.overwrite_mode then
		if not right or self.current_frame_only then
			actual_nudge_count = self:LimitNudgeCount(moho, frame, endFrame, global)
		end
	end
	-- *
	if actual_nudge_count < 1 then
		return
	end
	-- *
	if not right then
		actual_nudge_count = -1*actual_nudge_count
	end
	-- *
	if global then
		self:GlobalLayerShift(frame, moho, actual_nudge_count)
	else
		self:LayerShift(frame, moho, actual_nudge_count, layer)
	end
	-- * Also move cursor with frames
	if (global and self.global_move_cursor) or (not global and self.move_cursor) then
		if right or frame > -1*actual_nudge_count-1 then
			local newFrame = frame + actual_nudge_count
			moho:SetCurFrame(newFrame)
		end
	end
	-- * Also move end duration
	local endFrame = moho.document:EndFrame()
	if (global and self.global_move_endFrame) or (not global and self.move_endFrame) then
		moho.document:SetEndFrame(math.max(1, endFrame+actual_nudge_count))
	end
	-- * Also shift global channels (Only for Main timeline)
	if moho.layer:CurrentAction() == "" then
		if global or not LK_NudgeKeys.onlyNudgeDocumentChannelsOnGlobalNudges then
			local channels = {}
			FO_Channels:AddGlobalChannels(moho, channels)
			for i, channel in ipairs(channels) do
				self:ChannelShift(frame, channel, actual_nudge_count, 0)
			end
		end
	end
	-- * Update user interface
	moho:UpdateUI()
	MOHO.Redraw()
end

-- **************************************************
-- Limit Nudge Count, because we don't want to overwrite any existing keys.
-- **************************************************
function LK_NudgeKeys:LimitNudgeCount(moho, startFrame, endFrame, global)
	local nudge_count = math.abs(endFrame - startFrame)
	local channels = {}
	local layers = FO_Channels:PreferredLayers(moho)
	for i = 1, #layers do
		FO_Channels:AddLayerChannels(moho, layers[i], channels)
	end
	if global or not LK_NudgeKeys.onlyNudgeDocumentChannelsOnGlobalNudges then
		FO_Channels:AddGlobalChannels(moho, channels)
	end
	if startFrame < endFrame then
		startFrame = startFrame + 1
	else
		startFrame = startFrame - 1
	end
	local foundKey = FO_Channels:FindKey(moho, channels, startFrame, endFrame)
	if foundKey then
		moho:Click() -- * Make a clicking sound if we're nudging less than the user wants.
		local actual_nudge_count = math.min(math.abs(startFrame - foundKey), nudge_count)
		return actual_nudge_count
	else
		return nudge_count
	end
end

function LK_NudgeKeys:FilterByMarker(moho)
	local doFilter = false
	local channel = moho.document.fTimelineMarkers
	local keyCount = channel:CountKeys() - 1
	local keysFound = 0
	local endKey = channel:Duration()
	local frameNum = endKey
	while keysFound < keyCount do
		thisKey = channel:GetClosestKeyID(frameNum)
		local keyFrameNum = channel:GetKeyWhen(thisKey)
		keysFound = 1 + keysFound
		local interp = MOHO.InterpSetting:new_local()
		channel:GetKeyInterp(keyFrameNum, interp)
		if interp.tags == 7 then -- * 7 = Tan
			doFilter = true
			break
		end
		frameNum = keyFrameNum - 1
	end
	local keyVal = nil
	if doFilter then
		if channel:HasKey(moho.frame) then
			local interp = MOHO.InterpSetting:new_local()
			channel:GetKeyInterp(moho.frame, interp)
			if interp.tags == 7 then -- * 7 = Tan
				keyVal = channel:GetValue(moho.frame)
			end
		end
		if keyVal ~= nil then
			moho:LayersWindowSetSearchContext(1) -- 1 = LAYERWND_SEARCHCONTEXT_NAMECONTAINS
			moho:LayersWindowSetSearchContextValue(keyVal)
		end
	end
	if keyVal == nil then
		moho:LayersWindowSetSearchContext(0) -- 0 = LAYERWND_SEARCHCONTEXT_ALL
		moho:LayersWindowSetSearchContextValue("")
		moho:ShowLayerInLayersPalette(moho.layer)
	end
end

Icon
Nudge Keys
Listed

Author: Lukas View Script
Script type: Tool

Uploaded: May 22 2023, 08:59

Last modified: Sep 05 2023, 12:56

Script Version: 2.0

Nudge Keys Toolset
My version of Rudiger's Nudge Keys Toolset

Image
- It kind of assumes you're not using the sequencer.
- There's an issue with bezier curves?
This script, and all other scripts on this site are distributed as free software under the GNU General Public License 3.0 or later.
Downloads count: 1083