Timeslider controlled with the Mouse wheel


#1

Hi all!

I did a quicky search and found an unanswered thread on this two years back but I was wondering if it was possible to map the time slider movement to the mouse wheel.

Cheers!


#2

Hi,

check this out. Not a great solution yet, but a start non the less.


--******************************************************************************
--*
--*  MouseWheely v1.0
--*  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"
(
	local isOpen = false
	rollout ro_timeWheely "test"
	(
		dotNetControl cont "System.Windows.Forms.Control"

		on cont MouseWheel sender event do (
			sliderTime += event.delta/120
		)
		on cont lostFocus do (
			try (destroyDialog ro_timeWheely)catch()
		)
		
		on ro_timeWheely open do (
			cont.focus()
			isOpen = true
		)
		on ro_timeWheely close do (
			isOpen = false
			updateToolbarButtons()
		)
	)

	on isChecked do isOpen

	on execute do (
		if not isOpen then
			createDialog ro_timeWheely pos:[-10000,-10000]
		updateToolbarButtons()
	)
)

hOpe this helps,
o


#3

Hi there!

Tried running it at work but we’re using MAX8 here, maybe that’s the reason it isn’t working… In any case, what version of MAX was it designed to work with and how do I install it properly?

Cheers


#4

Hi,

It’s designed for max 9 and up, since it uses dotNet controls. I’ll look into making a max 8 version, if I can find a suitable activex control.

As for installation, just follow the instructions found in the header of the script.

cheers,
o


#5

I have wanted control of the mouse wheel for sooooo long. I changed your script to control the falloff of Soft Selection for Editable Poly.

simply $.falloff instead of sliderTime.

I’m sure it’s crude but it works great for me. Esp with Bobo’s Base Jump.

I would love to be able to have this function active via Ctrl + MouseWheel, or Ctrl + Alt etc.


#6

Yes indeedy!

Ofer, I just tried this at home and it works wonders. I’m soon purchasing a WACOM Bamboo tablet since I work better with a stylus and this script pretty much makes the buy all that better.

The Bamboo features a touch-wheel that behaves exactly like the one found on iPod’s and sends basic mouse wheel scroll data to different applications. I’ll be able to use this script along with the touch-wheel to scrub the timeslider in MAX now thanks to this so… thank you ever so much!

Now, on to being nitpicky if I may… Would there be any way to leave this function perpetually on? I don’t mind sacrificing the mouse-wheel zoom to be able to have your script running permanently since I zoom with CTRL+ALT+MMB as of now anyway…
Basically I’m asking if there’s anyway to bind a key to set the state of your script to a permanent ON state basically. Right now it seems to turn off whenever I click somewhere.

Cheers and thanks again!


#7

Hi,

Glad you like it.

As I said in my first post, this isn’t a perfect solution. The problem is, the dotNetControl has to have focus for it to catch the mouse wheel event. That’s why it can’t be on all the time, since when you click some place else, it losses focus and therefore stop firing the mouse wheel event.
Just thinking out loud here - one very ugly solutions could be to use a timer that will re-focus the dotNet control, but that will drive you nut. I don’t think you’ll be able to use max’s shortcut, or rename objects, because the focus from those fields we be stolen in favour of the the mouse wheel control.
I guess I’ll have to keep thinking about it.

In the mean time, here’s an updated version. I’m now using a struct s_MouseWheel, so if anyone else wants to use the mouse wheel for whatever, they just need to create an instace on the struct, register a callback fn for it (using the struct’s registerFn function), and start it.
Another thing I added is support for ctrl key. Press the ctrl key while scrolling the mouse wheel to increase/decrease the time slide jumps by 10 frames.

cheers,
o


--******************************************************************************
--*
--*  MouseWheely v1.2
--*  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.
--*
--******************************************************************************

--------- Struct definition
global s_MouseWheel
struct s_MouseWheel (
	message = undefined,
	func = undefined,
	valueDivider = 120,
	fn createRollout = 
	(
		rollout ro_MouseWheel "test" 
		(
			local lastTimeValue = sliderTime
			local onChangeFn
			local isActive = false
			local msg = false
			local divider = 120
			
			dotNetControl cont "System.Windows.Forms.Control"
			
			on cont MouseWheel sender event do (
				if isKindOf onChangeFn MAXScriptFunction then (
					onChangeFn (event.delta / divider)
				)
			)
			on cont lostFocus do (
				try (destroyDialog ro_MouseWheel)catch()
			)
			
			on ro_MouseWheel open do (
				isActive = true
				enableAccelerators = false
				cont.focus()
			)
			on ro_MouseWheel close do (
				if msg then
					popPrompt()
				isActive = false
				enableAccelerators = true
				updateToolbarButtons()
			)
		)
		ro_MouseWheel
	),
	roll = createRollout(),
	
	fn registerFn f =
	(
		func = f
		roll.onChangeFn = func
		if superclassOf valueDivider == Number then
			roll.divider = valueDivider
		else
			roll.divider = 120
		roll.msg = message != undefined 
	),
	
	fn unregisterFn =
	(
		registerFn undefined
	),
	
	fn start msg: =
	(
		if isKindOf func MAXScriptFunction then (
			if msg != unsupplied then
				message = msg
			try(destroyDialog roll)catch()
			createDialog roll pos:[-10000,-10000]
			registerFn func
			if message != undefined then (
				pushPrompt (message as string)
			)
			true
		) else (
			roll.isActive = false
			false
		)
	),
	
	fn isActive = 
	(
		isProperty roll #isActive and roll.isActive
	)
	
) -- end of s_MouseWheel struct


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

macroScript Os_timeWheely
	category:"Os Tools"
	tooltip:"Use mouse wheel to scroll time slider"
	buttontext:"TimeWheely"
(
	global s_MouseWheel
	local mouseWheel = s_MouseWheel()
	
	fn onMouseWheelChanged val =
	(
		if keyboard.controlPressed then
			val *= 10
		sliderTime += val
	)
	
	mouseWheel.registerFn onMouseWheelChanged

	on isChecked do mouseWheel.isActive()

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



#8

That’s great! I got a feeling the window focus was the issue while using it but it’s definitely worth having nonetheless. Ideally it would be set to ON all the time so you don’t have to keep entering a ‘timeslider wheel control mode’ so to speak.

I noticed this script isn’t on your site. Will you keep posting updates in this thread or can we expect to be able to follow your progress over at your site?

Again, many thanks! It’s seemingly-simple little scripts like yours that make using a 3D app all the more expressive!

Cheers!


#9

OK, I found a serious bug in the previous version I posted. When you re-open max you get a bunch of errors. This version solves this, plus I added some more features to the strcut in case anyone want’s to use it for their own stuff.

As for the timer idea, it was a bad one. You can’t work in max once it’s on, you can pretty much only scroll the time slider, and my guess is that’s not the only reason you have max open :slight_smile:
I need to find a way to fire a callback or event to easily fire up the script without being too obtrusive. I’ll keep thinking about it.

For now, I’ll keep posting the updates here, until I update it on my website (I’m just too lazy…)


 --******************************************************************************
 --*
 --*  MouseWheely v1.5
 --*  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.
 		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
 				
 
 				-- User Interface
 				--------------------------------------
 				dotNetControl cont "System.Windows.Forms.Control"
 				
 
 				-- Functions
 				--------------------------------------
 				fn init =
 				(
 					if isKindOf onActivateFn MAXScriptFunction then 
 						onActivateFn()
 					isActive = true
 					enableAccelerators = false
 					cont.focus()
 				)
 				
 				fn done =
 				(
 					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 (
 					alt = event.Alt
 					ctrl = event.Control
 					shift = event.Shift
 					event.SuppressKeyPress = true
 					event.Handled = false
 				)
 				on cont KeyUp sender event do (
 					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
 		),
 		
 		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 =
 		(
 			if not persistentWindow then
 				try(destroyDialog roll)catch()
 			else
 				roll.focus()
 		),
 		
 		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.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


#10

Fairly cool, thanks for posting (and updating)! I slowly start to look into .Net and this is yet another nice example why I should do it.

Do you think dotNet could also be used to do things ‘on key press’? For example, turn on snaps only while the S key is being pressed down and turn off snaps as soon as the S key is released?

Cheers
– MartinB


#11

I don’t have much experience with it either. The implementation in max is actually quite simple, the problem I find is figuring out what controls are available.

Do you think dotNet could also be used to do things ‘on key press’? For example, turn on snaps only while the S key is being pressed down and turn off snaps as soon as the S key is released?

Cheers
– MartinB

That’s where I’m stuck now with this script. The problem is how do I capture general events from input devices without having the focus on this control. So far, I didn’t find a way, that’s why you have to activate this mode (by setting the focus to the control). But having to activate it, kind of defets the purpose of what you wish to do.
If anyone has any idea how to capture global events, I’ll be happy to hear.

cheers,
o


#12

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.


#13

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.


#14

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


#15

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.


#16

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!


#17

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


#18

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


#19

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 :slight_smile:

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…?


#20

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