Notifications
Clear all

[Closed] Timeslider controlled with the Mouse wheel

Great script and great use of dotNet.

Haven’t delved into the code yet, but a bit of functionality that might be helpful is to have the option for then donNet control capture the hotkey that launched the script and when it gets a key up, turn itself off. This way it works like holding down the ctrl alt or shift key, only scrubbing while its key is down. It should be pretty straightforward, only I don’t know if it’s possible for the script to determine which key called it in the first place. I’ll look into it.

2 Replies
(@ofer_z)
Joined: 11 months ago

Posts: 0

Added a sticky mode, but it’s still not perfect. If you activate it with a keyboard shortcut, when you release the key it will exit the mouse wheel capture. However, there are still some usage quirks to iron out. Here’s what I got so far (usable, but annoying in some cases):


--******************************************************************************
--*
--*  MouseWheely v1.6
--*  by Ofer Zelichover (c) 02/2008
--*  www.oferz.com   maxscript@oferz.com
--*
--******************************************************************************
--*  You may use this script freely as you see fit.							*
--*  You may use parts or the script as a whole in your own scripts.		   *
--*  (it would be nice if you give me a credit if you do so ;))				*
--******************************************************************************
--*  This script comes with no waranty!										
--*  Although I tried this script and couldn't find any problems with it, I can,
--*  in no way be held responsible for any kind of loss or damage, whether	 
--*  direct or indirect, due to the use of this script.						
--*																			
--*  ********************************************************************	  
--*   IF YOU DON'T LIKE THE ABOVE STATEMENT, DON'T USE THIS SCRIPT 	  
--*  ********************************************************************	  
--*																			
--*  If you find any bugs in this script, please let me know.				  
--******************************************************************************
--* Description																*
--* -------------															  *
--* Allows the usage of the mouse wheel to scroll the time slider
--* 
--******************************************************************************
--* Isntallation and Usage:
--* ------------------------
--*  After you run this script, the script will appear in the
--*  "Os tools" category in the customize ui menu.
--*  Assign it to a keyboard shortcut/button/RC Menu
--*  After pressing the button/keyboard key/ RC menu item, use the mouse wheel
--*  to scroll the time slider. Click anywhere to stop the opration.
--*
--***************************************************************************


--*********************************************************
-- Start of macroScript
--************************************************************

macroScript Os_timeWheely
	category:"Os Tools"
	tooltip:"Use mouse wheel to scroll time slider"
	buttontext:"TimeWheely"
(
	--------- Struct definition
	global s_MouseWheel
	struct s_MouseWheel (
		message = undefined, 	-- The message that will be displayed in the status bar. 
								-- You can pass the message as a msg: parameter to the start function
								-- instead of setting it directly.
		onChangeFunc = undefined, 			-- The onChange callback function. DO NOT set it directly. Use registerFn instead.
		onActivateFunc = undefined, 		-- The onActivate callback function. DO NOT set it directly. Use registerFn instead.
		onDeactivateFunc = undefined, 		-- The onDeactivate callback function. DO NOT set it directly. Use registerFn instead.
		persistentWindow = false, 	-- If true, the window will always open (after the first start command).
									-- If false, the window will be closed when the control loses focus. This mode is slightly less
									-- effective (not noticable), but is safer.
		stickyMode = false, 		-- When this is true, the key value will stored when a key is first pressed. When the key value
									-- is changed the control will lose focus, thus ending the mouse wheel capture.
		valueDivider = 120,		-- The scroll wheel delta will be divided by this number before being passed to the callback.

		fn createRollout = 
		(
			rollout ro_MouseWheel "test" 
			(
				-- Local Variable Definitions
				--------------------------------------
				local onChangeFn, onActivateFn, onDeActivateFn
				local isActive = false
				local msg = false
				local divider = 120
				local persistentWin = false
				local alt = false, ctrl = false, shift = false
				local keyValue = undefined
				local invalidKeyValues = #{16..18}
				local sticky = true
				

				-- User Interface
				--------------------------------------
				label dummyCont pos:[0,0]
				dotNetControl cont "System.Windows.Forms.Control" pos:[0,0]
				

				-- Functions
				--------------------------------------
				fn stop =
				(
					if persistentWin then
						setFocus dummyCont
					else
						try(destroyDialog ro_MouseWheel)catch()
				)
				fn init =
				(
					if isKindOf onActivateFn MAXScriptFunction then 
						onActivateFn()
					isActive = true
					enableAccelerators = false
					cont.focus()
				)
				
				fn done =
				(
					keyValue = undefined
					if msg then (
						popPrompt()
						msg = false
					)
					isActive = false
					enableAccelerators = true
					if isKindOf onDeActivateFn MAXScriptFunction then 
						onDeActivateFn()
				)

				
				-- Event Handlers
				----------------------------------------
				on cont MouseWheel sender event do (
					if isKindOf onChangeFn MAXScriptFunction then (
						onChangeFn (event.delta / divider) alt:alt ctrl:ctrl shift:shift
					)
				)
				on cont lostFocus do (
					if not persistentWin then
						try (destroyDialog ro_MouseWheel)catch()
					else
						done()
				)
				on cont KeyDown sender event do (
					if sticky then (
						if keyValue == undefined and not invalidKeyValues[event.KeyValue] then
							keyValue = event.KeyValue
					)
					alt = event.Alt
					ctrl = event.Control
					shift = event.Shift
					event.SuppressKeyPress = true
					event.Handled = false
				)
				on cont KeyUp sender event do (
					if sticky then (
						if keyValue == event.KeyValue then
							stop()
					)
					alt = event.Alt
					ctrl = event.Control
					shift = event.Shift
					event.SuppressKeyPress = true
					event.Handled = false
				)

				on ro_MouseWheel open do init()
				on ro_MouseWheel close do done()
			) -- end of ro_MouseWheel rollout
			
			-- Return the rollout
			ro_MouseWheel
		),
		roll = createRollout(),
		
		fn setRollParams =
		(
			roll.onChangeFn = onChangeFunc
			roll.onActivateFn = onActivateFunc
			roll.onDeActivateFn = onDeactivateFunc
			if superclassOf valueDivider == Number then
				roll.divider = valueDivider
			else
				roll.divider = 120
			roll.msg = message != undefined 
			roll.persistentWin = persistentWindow
			roll.sticky = stickyMode
		),
		
		fn registerFn onChange: onActivate: onDeactivate: =
		(
			local changed = false
			if isKindOf onChange MAXScriptFunction then (
				onChangeFunc = onChange
				changed = true
			)
			if isKindOf onActivate MAXScriptFunction then (
				onActivateFunc = onActivate
				changed = true
			)
			if isKindOf onDeactivate MAXScriptFunction then (
				onDeactivateFunc = onDeactivate
				changed = true
			)
			changed
		),
		
		fn unregisterFn type =
		(
			type = (type as string) as name
			case type of (
				#onChange: 		onChangeFunc = undefined
				#onActivate: 	onActivateFunc = undefined
				#onDeactivate: 	onDeactivateFunc = undefined
				#all: 			(onChangeFunc = undefined; onActivateFunc = undefined; onDeactivateFunc = undefined)
			)
			setRollParams()
			true
		),
		
		fn start msg: =
		(
			if isKindOf onChangeFunc MAXScriptFunction then (
				if msg != unsupplied then
					message = msg
				if not persistentWindow then
					try(destroyDialog roll)catch()
				if not persistentWindow or not roll.open then
					createDialog roll pos:[-10000,-10000]
				setRollParams()
				if message != undefined then (
					pushPrompt (message as string)
				)
				if persistentWindow then
					roll.init()
				true
			) else (
				roll.isActive = false
				false
			)
		),
		
		fn stop =
		(
			try(roll.stop())catch()
		),
		
		fn isActive = 
		(
			isProperty roll #cont and isProperty roll.cont #Focused and roll.cont.Focused
		)
		
	) -- end of s_MouseWheel struct
------------------------------------------------------------------------------


	
	-- Initialize the mouseWheel struct
	local mouseWheel = s_MouseWheel()
	
	fn onMouseWheelChanged val alt: ctrl: shift: =
	(
		if ctrl then
			val *= 10
		sliderTime += val
	)
	fn onDeAct = updateToolbarButtons()
	
	mouseWheel.persistentWindow = true
	mouseWheel.stickyMode = true
	mouseWheel.registerFn onChange:onMouseWheelChanged onDeactivate:onDeAct
	
	
	-- MacroScript Event Handlers
	---------------------------------------
	on isChecked do mouseWheel.isActive()

	on execute do (
		if mouseWheel.isActive() then (
			mouseWheel.stop()
		) else (
			mouseWheel.start msg:"Scroll mouse wheel to slide the time slider"
		)
		updateToolbarButtons()
	)
) -- end of Os_timeWheely macroscript


cheers,
o

(@focomoso)
Joined: 11 months ago

Posts: 0

Right, sticky mode, that’s the name. Nothing is ever perfect. Usable is all we can ask. And since you’re doing this out of the kindness of your heart, it’s asking a lot…

Thanks.

Hmm… this has me thinking. Anyone know if it’s possible to capture spaceNavigator or spacePilot (or explorer, I get all the names mixed up) data in a dot net? When I’m doing lip sync, I’d love to be able to scrub the timeline with my spaceThingie.

Great updates man! Although it seems to be stuck in +/- 10 frames mode now when assigned to a keyboard shortcut, is this normal?

EDIT: Actually, it’s working fine with my mouse wheel but not with my wacom’s scroll wheel… I guess this is becase the amount of ‘turning’ the wheel that’s required to trigger data is more sensitive.

How would I go about playing with a sensibility threshold value?

Cheers!

OK, I added support for settings in an ini file. The file should be placed in the 3dsmax_root\scripts folder, and in should be named mouseWheely.ini
For now there are only two options available:
StickyMode – setting this to false will turn off the sticky mode.
Divider – defines the number by which the raw value from the mouse wheel is divided. A lower value will cause a more “sensitive” scroll.
Here’s an example that will mimic the defaults (if no file is defined, or an option is not defined, the default for that option is used):


[Settings]
Divider=120
StickyMode=true

Here’s the new version:


--******************************************************************************
--*
--*  MouseWheely v1.7
--*  by Ofer Zelichover (c) 02/2008
--*  www.oferz.com   maxscript@oferz.com
--*
--******************************************************************************
--*  You may use this script freely as you see fit.							*
--*  You may use parts or the script as a whole in your own scripts.		   *
--*  (it would be nice if you give me a credit if you do so ;))				*
--******************************************************************************
--*  This script comes with no waranty!										
--*  Although I tried this script and couldn't find any problems with it, I can,
--*  in no way be held responsible for any kind of loss or damage, whether	 
--*  direct or indirect, due to the use of this script.						
--*																			
--*  ********************************************************************	  
--*   IF YOU DON'T LIKE THE ABOVE STATEMENT, DON'T USE THIS SCRIPT 	  
--*  ********************************************************************	  
--*																			
--*  If you find any bugs in this script, please let me know.				  
--******************************************************************************
--* Description																*
--* -------------															  *
--* Allows the usage of the mouse wheel to scroll the time slider
--* 
--******************************************************************************
--* Isntallation and Usage:
--* ------------------------
--*  After you run this script, the script will appear in the
--*  "Os tools" category in the customize ui menu.
--*  Assign it to a keyboard shortcut/button/RC Menu
--*  After pressing the button/keyboard key/ RC menu item, use the mouse wheel
--*  to scroll the time slider. Click anywhere to stop the opration.
--*
--***************************************************************************


--*********************************************************
-- Start of macroScript
--************************************************************

macroScript Os_timeWheely
	category:"Os Tools"
	tooltip:"Use mouse wheel to scroll time slider"
	buttontext:"TimeWheely"
(
	--------- Struct definition
	global s_MouseWheel
	struct s_MouseWheel (
		message = undefined, 	-- The message that will be displayed in the status bar. 
								-- You can pass the message as a msg: parameter to the start function
								-- instead of setting it directly.
		onChangeFunc = undefined, 			-- The onChange callback function. DO NOT set it directly. Use registerFn instead.
		onActivateFunc = undefined, 		-- The onActivate callback function. DO NOT set it directly. Use registerFn instead.
		onDeactivateFunc = undefined, 		-- The onDeactivate callback function. DO NOT set it directly. Use registerFn instead.
		persistentWindow = false, 	-- If true, the window will always open (after the first start command).
									-- If false, the window will be closed when the control loses focus. This mode is slightly less
									-- effective (not noticable), but is safer.
		stickyMode = false, 		-- When this is true, the key value will stored when a key is first pressed. When the key value
									-- is changed the control will lose focus, thus ending the mouse wheel capture.
		valueDivider = 120.,		-- The scroll wheel delta will be divided by this number before being passed to the callback.

		fn createRollout = 
		(
			rollout ro_MouseWheel "test" 
			(
				-- Local Variable Definitions
				--------------------------------------
				local onChangeFn, onActivateFn, onDeActivateFn
				local isActive = false
				local msg = false
				local divider = 120.
				local persistentWin = false
				local alt = false, ctrl = false, shift = false
				local keyValue = undefined
				local invalidKeyValues = #{16..18}
				local sticky = true
				

				-- User Interface
				--------------------------------------
				dotNetControl cont "System.Windows.Forms.Control" pos:[0,0]
				

				-- Functions
				--------------------------------------
				fn stop =
				(
					if persistentWin then
						cont.enabled = false
					else
						try(destroyDialog ro_MouseWheel)catch()
				)
				fn init =
				(
					if isKindOf onActivateFn MAXScriptFunction then 
						onActivateFn()
					isActive = true
					enableAccelerators = false
					cont.enabled = true
					cont.focus()
				)
				
				fn done =
				(
					keyValue = undefined
					if msg then (
						popPrompt()
						msg = false
					)
					isActive = false
					enableAccelerators = true
					if isKindOf onDeActivateFn MAXScriptFunction then 
						onDeActivateFn()
				)

				
				-- Event Handlers
				----------------------------------------
				on cont MouseWheel sender event do (
					if isKindOf onChangeFn MAXScriptFunction then (
						onChangeFn (event.delta / divider) alt:alt ctrl:ctrl shift:shift
					)
				)
				on cont lostFocus do (
					if not persistentWin then
						try (destroyDialog ro_MouseWheel)catch()
					else
						done()
				)
				on cont KeyDown sender event do (
					if sticky then (
						if keyValue == undefined and not invalidKeyValues[event.KeyValue] then
							keyValue = event.KeyValue
					)
					alt = event.Alt
					ctrl = event.Control
					shift = event.Shift
					event.SuppressKeyPress = true
					event.Handled = false
				)
				on cont KeyUp sender event do (
					if sticky then (
						if keyValue == event.KeyValue then
							stop()
					)
					alt = event.Alt
					ctrl = event.Control
					shift = event.Shift
					event.SuppressKeyPress = true
					event.Handled = false
				)

				on ro_MouseWheel open do init()
				on ro_MouseWheel close do done()
			) -- end of ro_MouseWheel rollout
			
			-- Return the rollout
			ro_MouseWheel
		),
		roll = createRollout(),
		
		fn setRollParams =
		(
			roll.onChangeFn = onChangeFunc
			roll.onActivateFn = onActivateFunc
			roll.onDeActivateFn = onDeactivateFunc
			if superclassOf valueDivider == Number then
				roll.divider = valueDivider
			else
				roll.divider = 120
			roll.msg = message != undefined 
			roll.persistentWin = persistentWindow
			roll.sticky = stickyMode
		),
		
		fn registerFn onChange: onActivate: onDeactivate: =
		(
			local changed = false
			if isKindOf onChange MAXScriptFunction then (
				onChangeFunc = onChange
				changed = true
			)
			if isKindOf onActivate MAXScriptFunction then (
				onActivateFunc = onActivate
				changed = true
			)
			if isKindOf onDeactivate MAXScriptFunction then (
				onDeactivateFunc = onDeactivate
				changed = true
			)
			changed
		),
		
		fn unregisterFn type =
		(
			type = (type as string) as name
			case type of (
				#onChange: 		onChangeFunc = undefined
				#onActivate: 	onActivateFunc = undefined
				#onDeactivate: 	onDeactivateFunc = undefined
				#all: 			(onChangeFunc = undefined; onActivateFunc = undefined; onDeactivateFunc = undefined)
			)
			setRollParams()
			true
		),
		
		fn start msg: =
		(
			if isKindOf onChangeFunc MAXScriptFunction then (
				if msg != unsupplied then
					message = msg
				if not persistentWindow then
					try(destroyDialog roll)catch()
				if not persistentWindow or not roll.open then
					createDialog roll pos:[-10000,-10000]
				setRollParams()
				if message != undefined then (
					pushPrompt (message as string)
				)
				if persistentWindow then
					roll.init()
				true
			) else (
				roll.isActive = false
				false
			)
		),
		
		fn stop =
		(
			try(roll.stop())catch()
		),
		
		fn isActive = 
		(
			isProperty roll #cont and isProperty roll.cont #Focused and roll.cont.Focused
		)
		
	) -- end of s_MouseWheel struct
------------------------------------------------------------------------------


	
	-- Initialize the mouseWheel struct
	local mouseWheel = s_MouseWheel()
	local iniFile = getDir #Scripts + "\\mouseWheely.ini"
	
	fn onMouseWheelChanged val alt: ctrl: shift: =
	(
		val = val as integer
		if ctrl then
			val *= 10
		sliderTime += val
	)
	fn onDeAct = updateToolbarButtons()
	
	mouseWheel.persistentWindow = true
	mouseWheel.stickyMode = ((getINISetting iniFile "Settings" "StickyMode") as name) != #false
	local val = getINISetting iniFile "Settings" "Divider"
	if val != "" and val as float != undefined then
		mouseWheel.valueDivider = val as float
	mouseWheel.registerFn onChange:onMouseWheelChanged onDeactivate:onDeAct
	
	
	-- MacroScript Event Handlers
	---------------------------------------
	on isChecked do mouseWheel.isActive()

	on execute do (
		if mouseWheel.isActive() then (
			mouseWheel.stop()
		) else (
			mouseWheel.start msg:"Scroll mouse wheel to slide the time slider"
		)
		updateToolbarButtons()
	)
) -- end of Os_timeWheely macroscript


enjOy,
o

Just a “practical” answer… I know that doing it in maxscript is much more of a challenge but if you just reaaaaaaaally want just the end result… I think that a mouse remapper would suffice, logitech’s drivers for example allow that for specific applications, so you can just turn it on in max…

hides at the incoming punches

punch!

I love working in Max with a tablet and I know that many people do, so getting the touch-wheel/touch-strip to move the time slider is pretty neat IMHO

Cheers

EDIT: Just one question; how to use the .ini file properly? Should the settings update each time the .ini is saved or are we required to restard MAX or run the script again…?

Added another option to the INI file:
useModifierKeys – when this setting is set to false, the ctrl key will not cause a 10f jump. Default: true.


--******************************************************************************
--*
--*  MouseWheely v1.8
--*  by Ofer Zelichover (c) 02/2008
--*  www.oferz.com   maxscript@oferz.com
--*
--******************************************************************************
--*  You may use this script freely as you see fit.							*
--*  You may use parts or the script as a whole in your own scripts.		   *
--*  (it would be nice if you give me a credit if you do so ;))				*
--******************************************************************************
--*  This script comes with no waranty!										
--*  Although I tried this script and couldn't find any problems with it, I can,
--*  in no way be held responsible for any kind of loss or damage, whether	 
--*  direct or indirect, due to the use of this script.						
--*																			
--*  ********************************************************************	  
--*   IF YOU DON'T LIKE THE ABOVE STATEMENT, DON'T USE THIS SCRIPT 	  
--*  ********************************************************************	  
--*																			
--*  If you find any bugs in this script, please let me know.				  
--******************************************************************************
--* Description																*
--* -------------															  *
--* Allows the usage of the mouse wheel to scroll the time slider
--* 
--******************************************************************************
--* Isntallation and Usage:
--* ------------------------
--*  After you run this script, the script will appear in the
--*  "Os tools" category in the customize ui menu.
--*  Assign it to a keyboard shortcut/button/RC Menu
--*  After pressing the button/keyboard key/ RC menu item, use the mouse wheel
--*  to scroll the time slider. Click anywhere to stop the opration.
--*
--***************************************************************************


--*********************************************************
-- Start of macroScript
--************************************************************

macroScript Os_timeWheely
	category:"Os Tools"
	tooltip:"Use mouse wheel to scroll time slider"
	buttontext:"TimeWheely"
(
	--------- Struct definition
	global s_MouseWheel
	struct s_MouseWheel (
		message = undefined, 	-- The message that will be displayed in the status bar. 
								-- You can pass the message as a msg: parameter to the start function
								-- instead of setting it directly.
		onChangeFunc = undefined, 			-- The onChange callback function. DO NOT set it directly. Use registerFn instead.
		onActivateFunc = undefined, 		-- The onActivate callback function. DO NOT set it directly. Use registerFn instead.
		onDeactivateFunc = undefined, 		-- The onDeactivate callback function. DO NOT set it directly. Use registerFn instead.
		persistentWindow = false, 	-- If true, the window will always open (after the first start command).
									-- If false, the window will be closed when the control loses focus. This mode is slightly less
									-- effective (not noticable), but is safer.
		stickyMode = false, 		-- When this is true, the key value will stored when a key is first pressed. When the key value
									-- is changed the control will lose focus, thus ending the mouse wheel capture.
		valueDivider = 120.,		-- The scroll wheel delta will be divided by this number before being passed to the callback.

		fn createRollout = 
		(
			rollout ro_MouseWheel "test" 
			(
				-- Local Variable Definitions
				--------------------------------------
				local onChangeFn, onActivateFn, onDeActivateFn
				local isActive = false
				local msg = false
				local divider = 120.
				local persistentWin = false
				local alt = false, ctrl = false, shift = false
				local keyValue = undefined
				local invalidKeyValues = #{16..18}
				local sticky = true
				

				-- User Interface
				--------------------------------------
				dotNetControl cont "System.Windows.Forms.Control" pos:[0,0]
				

				-- Functions
				--------------------------------------
				fn stop =
				(
					if persistentWin then
						cont.enabled = false
					else
						try(destroyDialog ro_MouseWheel)catch()
				)
				fn init =
				(
					if isKindOf onActivateFn MAXScriptFunction then 
						onActivateFn()
					isActive = true
					enableAccelerators = false
					cont.enabled = true
					cont.focus()
				)
				
				fn done =
				(
					keyValue = undefined
					if msg then (
						popPrompt()
						msg = false
					)
					isActive = false
					enableAccelerators = true
					if isKindOf onDeActivateFn MAXScriptFunction then 
						onDeActivateFn()
				)

				
				-- Event Handlers
				----------------------------------------
				on cont MouseWheel sender event do (
					if isKindOf onChangeFn MAXScriptFunction then (
						onChangeFn (event.delta / divider) alt:alt ctrl:ctrl shift:shift
					)
				)
				on cont lostFocus do (
					if not persistentWin then
						try (destroyDialog ro_MouseWheel)catch()
					else
						done()
				)
				on cont KeyDown sender event do (
					if sticky then (
						if keyValue == undefined and not invalidKeyValues[event.KeyValue] then
							keyValue = event.KeyValue
					)
					alt = event.Alt
					ctrl = event.Control
					shift = event.Shift
					event.SuppressKeyPress = true
					event.Handled = false
				)
				on cont KeyUp sender event do (
					if sticky then (
						if keyValue == event.KeyValue then
							stop()
					)
					alt = event.Alt
					ctrl = event.Control
					shift = event.Shift
					event.SuppressKeyPress = true
					event.Handled = false
				)

				on ro_MouseWheel open do init()
				on ro_MouseWheel close do done()
			) -- end of ro_MouseWheel rollout
			
			-- Return the rollout
			ro_MouseWheel
		),
		roll = createRollout(),
		
		fn setRollParams =
		(
			roll.onChangeFn = onChangeFunc
			roll.onActivateFn = onActivateFunc
			roll.onDeActivateFn = onDeactivateFunc
			if superclassOf valueDivider == Number then
				roll.divider = valueDivider
			else
				roll.divider = 120
			roll.msg = message != undefined 
			roll.persistentWin = persistentWindow
			roll.sticky = stickyMode
		),
		
		fn registerFn onChange: onActivate: onDeactivate: =
		(
			local changed = false
			if isKindOf onChange MAXScriptFunction then (
				onChangeFunc = onChange
				changed = true
			)
			if isKindOf onActivate MAXScriptFunction then (
				onActivateFunc = onActivate
				changed = true
			)
			if isKindOf onDeactivate MAXScriptFunction then (
				onDeactivateFunc = onDeactivate
				changed = true
			)
			changed
		),
		
		fn unregisterFn type =
		(
			type = (type as string) as name
			case type of (
				#onChange: 		onChangeFunc = undefined
				#onActivate: 	onActivateFunc = undefined
				#onDeactivate: 	onDeactivateFunc = undefined
				#all: 			(onChangeFunc = undefined; onActivateFunc = undefined; onDeactivateFunc = undefined)
			)
			setRollParams()
			true
		),
		
		fn start msg: =
		(
			if isKindOf onChangeFunc MAXScriptFunction then (
				if msg != unsupplied then
					message = msg
				if not persistentWindow then
					try(destroyDialog roll)catch()
				if not persistentWindow or not roll.open then
					createDialog roll pos:[-10000,-10000]
				setRollParams()
				if message != undefined then (
					pushPrompt (message as string)
				)
				if persistentWindow then
					roll.init()
				true
			) else (
				roll.isActive = false
				false
			)
		),
		
		fn stop =
		(
			try(roll.stop())catch()
		),
		
		fn isActive = 
		(
			isProperty roll #cont and isProperty roll.cont #Focused and roll.cont.Focused
		)
		
	) -- end of s_MouseWheel struct
------------------------------------------------------------------------------


	
	-- Initialize the mouseWheel struct
	local mouseWheel = s_MouseWheel()
	local iniFile = getDir #Scripts + "\\mouseWheely.ini"
	local useModifierKeys = ((getINISetting iniFile "Settings" "useModifierKeys") as name) != #false
	
	fn onMouseWheelChanged val alt: ctrl: shift: =
	(
		val = val as integer
		if useModifierKeys and ctrl then
			val *= 10
		sliderTime += val
	)
	fn onDeAct = updateToolbarButtons()
	
	mouseWheel.persistentWindow = true
	mouseWheel.stickyMode = ((getINISetting iniFile "Settings" "StickyMode") as name) != #false
	local val = getINISetting iniFile "Settings" "Divider"
	if val != "" and val as float != undefined then
		mouseWheel.valueDivider = val as float
	mouseWheel.registerFn onChange:onMouseWheelChanged onDeactivate:onDeAct
	
	
	-- MacroScript Event Handlers
	---------------------------------------
	on isChecked do mouseWheel.isActive()

	on execute do (
		if mouseWheel.isActive() then (
			mouseWheel.stop()
		) else (
			mouseWheel.start msg:"Scroll mouse wheel to slide the time slider"
		)
		updateToolbarButtons()
	)
) -- end of Os_timeWheely macroscript


cheers,
o

Another small update, this one makes the changes in the INI file immediate, not requiring a max restart.


--******************************************************************************
--*
--*  MouseWheely v1.81
--*  by Ofer Zelichover (c) 02/2008
--*  www.oferz.com   maxscript@oferz.com
--*
--******************************************************************************
--*  You may use this script freely as you see fit.							*
--*  You may use parts or the script as a whole in your own scripts.		   *
--*  (it would be nice if you give me a credit if you do so ;))				*
--******************************************************************************
--*  This script comes with no waranty!										
--*  Although I tried this script and couldn't find any problems with it, I can,
--*  in no way be held responsible for any kind of loss or damage, whether	 
--*  direct or indirect, due to the use of this script.						
--*																			
--*  ********************************************************************	  
--*   IF YOU DON'T LIKE THE ABOVE STATEMENT, DON'T USE THIS SCRIPT 	  
--*  ********************************************************************	  
--*																			
--*  If you find any bugs in this script, please let me know.				  
--******************************************************************************
--* Description																*
--* -------------															  *
--* Allows the usage of the mouse wheel to scroll the time slider
--* 
--******************************************************************************
--* Isntallation and Usage:
--* ------------------------
--*  After you run this script, the script will appear in the
--*  "Os tools" category in the customize ui menu.
--*  Assign it to a keyboard shortcut/button/RC Menu
--*  After pressing the button/keyboard key/ RC menu item, use the mouse wheel
--*  to scroll the time slider. Click anywhere to stop the opration.
--*
--***************************************************************************


--*********************************************************
-- Start of macroScript
--************************************************************

macroScript Os_timeWheely
	category:"Os Tools"
	tooltip:"Use mouse wheel to scroll time slider"
	buttontext:"TimeWheely"
(
	--------- Struct definition
	global s_MouseWheel
	struct s_MouseWheel (
		message = undefined, 	-- The message that will be displayed in the status bar. 
								-- You can pass the message as a msg: parameter to the start function
								-- instead of setting it directly.
		onChangeFunc = undefined, 			-- The onChange callback function. DO NOT set it directly. Use registerFn instead.
		onActivateFunc = undefined, 		-- The onActivate callback function. DO NOT set it directly. Use registerFn instead.
		onDeactivateFunc = undefined, 		-- The onDeactivate callback function. DO NOT set it directly. Use registerFn instead.
		persistentWindow = false, 	-- If true, the window will always open (after the first start command).
									-- If false, the window will be closed when the control loses focus. This mode is slightly less
									-- effective (not noticable), but is safer.
		stickyMode = false, 		-- When this is true, the key value will stored when a key is first pressed. When the key value
									-- is changed the control will lose focus, thus ending the mouse wheel capture.
		valueDivider = 120.,		-- The scroll wheel delta will be divided by this number before being passed to the callback.

		fn createRollout = 
		(
			rollout ro_MouseWheel "test" 
			(
				-- Local Variable Definitions
				--------------------------------------
				local onChangeFn, onActivateFn, onDeActivateFn
				local isActive = false
				local msg = false
				local divider = 120.
				local persistentWin = false
				local alt = false, ctrl = false, shift = false
				local keyValue = undefined
				local invalidKeyValues = #{16..18}
				local sticky = true
				

				-- User Interface
				--------------------------------------
				dotNetControl cont "System.Windows.Forms.Control" pos:[0,0]
				

				-- Functions
				--------------------------------------
				fn stop =
				(
					if persistentWin then
						cont.enabled = false
					else
						try(destroyDialog ro_MouseWheel)catch()
				)
				fn init =
				(
					if isKindOf onActivateFn MAXScriptFunction then 
						onActivateFn()
					isActive = true
					enableAccelerators = false
					cont.enabled = true
					cont.focus()
				)
				
				fn done =
				(
					keyValue = undefined
					if msg then (
						popPrompt()
						msg = false
					)
					isActive = false
					enableAccelerators = true
					if isKindOf onDeActivateFn MAXScriptFunction then 
						onDeActivateFn()
				)

				
				-- Event Handlers
				----------------------------------------
				on cont MouseWheel sender event do (
					if isKindOf onChangeFn MAXScriptFunction then (
						onChangeFn (event.delta / divider) alt:alt ctrl:ctrl shift:shift
					)
				)
				on cont lostFocus do (
					if not persistentWin then
						try (destroyDialog ro_MouseWheel)catch()
					else
						done()
				)
				on cont KeyDown sender event do (
					if sticky then (
						if keyValue == undefined and not invalidKeyValues[event.KeyValue] then
							keyValue = event.KeyValue
					)
					alt = event.Alt
					ctrl = event.Control
					shift = event.Shift
					event.SuppressKeyPress = true
					event.Handled = false
				)
				on cont KeyUp sender event do (
					if sticky then (
						if keyValue == event.KeyValue then
							stop()
					)
					alt = event.Alt
					ctrl = event.Control
					shift = event.Shift
					event.SuppressKeyPress = true
					event.Handled = false
				)

				on ro_MouseWheel open do init()
				on ro_MouseWheel close do done()
			) -- end of ro_MouseWheel rollout
			
			-- Return the rollout
			ro_MouseWheel
		),
		roll = createRollout(),
		
		fn setRollParams =
		(
			roll.onChangeFn = onChangeFunc
			roll.onActivateFn = onActivateFunc
			roll.onDeActivateFn = onDeactivateFunc
			if superclassOf valueDivider == Number then
				roll.divider = valueDivider
			else
				roll.divider = 120
			roll.msg = message != undefined 
			roll.persistentWin = persistentWindow
			roll.sticky = stickyMode
		),
		
		fn registerFn onChange: onActivate: onDeactivate: =
		(
			local changed = false
			if isKindOf onChange MAXScriptFunction then (
				onChangeFunc = onChange
				changed = true
			)
			if isKindOf onActivate MAXScriptFunction then (
				onActivateFunc = onActivate
				changed = true
			)
			if isKindOf onDeactivate MAXScriptFunction then (
				onDeactivateFunc = onDeactivate
				changed = true
			)
			changed
		),
		
		fn unregisterFn type =
		(
			type = (type as string) as name
			case type of (
				#onChange: 		onChangeFunc = undefined
				#onActivate: 	onActivateFunc = undefined
				#onDeactivate: 	onDeactivateFunc = undefined
				#all: 			(onChangeFunc = undefined; onActivateFunc = undefined; onDeactivateFunc = undefined)
			)
			setRollParams()
			true
		),
		
		fn start msg: =
		(
			if isKindOf onChangeFunc MAXScriptFunction then (
				if msg != unsupplied then
					message = msg
				if not persistentWindow then
					try(destroyDialog roll)catch()
				if not persistentWindow or not roll.open then
					createDialog roll pos:[-10000,-10000]
				setRollParams()
				if message != undefined then (
					pushPrompt (message as string)
				)
				if persistentWindow then
					roll.init()
				true
			) else (
				roll.isActive = false
				false
			)
		),
		
		fn stop =
		(
			try(roll.stop())catch()
		),
		
		fn isActive = 
		(
			isProperty roll #cont and isProperty roll.cont #Focused and roll.cont.Focused
		)
		
	) -- end of s_MouseWheel struct
------------------------------------------------------------------------------


	
	-- Initialize the mouseWheel struct
	local mouseWheel = s_MouseWheel()
	local iniFile = getDir #Scripts + "\\mouseWheely.ini"
	local useModifierKeys = ((getINISetting iniFile "Settings" "useModifierKeys") as name) != #false
	
	fn onMouseWheelChanged val alt: ctrl: shift: =
	(
		val = val as integer
		if useModifierKeys and ctrl then
			val *= 10
		sliderTime += val
	)
	fn onDeAct = updateToolbarButtons()
	
	mouseWheel.persistentWindow = true
	mouseWheel.stickyMode = ((getINISetting iniFile "Settings" "StickyMode") as name) != #false
	local val = getINISetting iniFile "Settings" "Divider"
	if val != "" and val as float != undefined then
		mouseWheel.valueDivider = val as float
	mouseWheel.registerFn onChange:onMouseWheelChanged onDeactivate:onDeAct
	
	
	-- MacroScript Event Handlers
	---------------------------------------
	on isChecked do mouseWheel.isActive()

	on execute do (
		useModifierKeys = ((getINISetting iniFile "Settings" "useModifierKeys") as name) != #false
		mouseWheel.stickyMode = ((getINISetting iniFile "Settings" "StickyMode") as name) != #false
		local val = getINISetting iniFile "Settings" "Divider"
		if val != "" and val as float != undefined then
			mouseWheel.valueDivider = val as float

		if mouseWheel.isActive() then (
			mouseWheel.stop()
		) else (
			mouseWheel.start msg:"Scroll mouse wheel to slide the time slider"
		)
		updateToolbarButtons()
	)
) -- end of Os_timeWheely macroscript


cheers,
o

Just adding onto the praise of how awesome this is. I have absolutely no use for a scrolly timeline but I’m already plotting how to hack it into a million other useful things.

It seems like the next logical step is to rewrite this as a function which can be called in any other script sort of like:

 fn Zoomout = 
(
ZoomOut()
)
 
fn ZoomIn = 
(
ZoomIn()
)
 
fn Pan =
(
Pan
)

scrolliewheely Zoomin Zoomout Pan

Obviously the code is all fake. But just a way to activate it along with what function to do when that event is called.

Hi,

Actually it’s very easy to do already, that’s why I created the core functionality in a struct.
Here’s a small (and useless) demo of how to use the struct to control the radius of the selected objects with the mouse wheel. Just past in a new script window and press ctrl+e


(
	global s_MouseWheel
	local scroller = s_MouseWheel()
	local paramsHolder = #()
	
	fn onStart = 
	(
		paramsHolder = #()
		for o in selection do (
			append paramsHolder o.wireColor
			o.wireColor = red
		)
	)
	fn onEnd =
	(
		for i = 1 to selection.count do
			selection[i].wireColor = paramsHolder[i]
	)
	fn onChange val ctrl: alt: shift: = 
	(
		for o in selection where isProperty o #Radius do
			o.radius += val
	)
	
	scroller.persistentWindow = true
	scroller.registerFn onChange:onChange onActivate:onStart onDeactivate:onEnd
	scroller.start msg:"Use mouse wheel to change radius."
	
)

cheers,
o

Page 2 / 3