/** * SHORTCUTS * This plugin enables keyboard shortcuts. * * Kurt George Gjerde 2009 * Licence: GNU GPL **/ package { import com.jeroenwijering.events.*; import flash.display.MovieClip; //import flash.text.TextField; import flash.events.KeyboardEvent; import flash.ui.Keyboard; import flash.utils.Timer; import flash.events.TimerEvent; import flash.events.MouseEvent; public class Shortcuts extends MovieClip implements PluginInterface { /** Reference to this plugins' flashvars. **/ public var config:Object = { displayclick:'none', step:-1, steps:10, minstep:5, maxstep:30, slowmotion:false, slowmotionvolume:1 }; /** Reference to this plugins' stage graphics. **/ public var clip:MovieClip; /** Reference to the View of the player. **/ private var view:AbstractView; /** globals **/ private var version:String = '1.0'; private var debug:Boolean = false; /** Position and duration **/ private var position:Number = 0; private var duration:Number = 0; /** Waiting for pause; pause at next TIME event **/ private var waitingForPause:Boolean = false; /** Seek **/ private var seekInAction:Boolean = false; private var seekPrePosition:Number; private var seekTriedStep:Number; private var stepsize:Number; /** Slow motion **/ private var slowmoOn:Boolean = false; private var slowmoCount:uint = 0; private var slowmoDelay:Number = 50; private var slowmoTimer:Timer; private var slowmoProg:Number; private var slowmoVolume:Number; /** Keyboard states **/ private var keyShift:Boolean = false; private var keyCtrl:Boolean = false; /** Icons **/ private var iconsEnabled:Boolean = false; /** Constructor. **/ public function Shortcuts():void { clip = this; // hide displayclick replacement displayclickButton.x = 0; displayclickButton.y = 0; displayclickButton.width = 0; displayclickButton.height = 0; }; /** Initializer **/ public function initializePlugin(vie:AbstractView):void { view = vie; // config if (view.config['shortcuts.displayclick']) { config['displayclick'] = view.config['shortcuts.displayclick']; } if (view.config['shortcuts.step']) { config['step'] = view.config['shortcuts.step']; } if (view.config['shortcuts.steps']) { config['steps'] = view.config['shortcuts.steps']; } if (view.config['shortcuts.minstep']) { config['minstep'] = view.config['shortcuts.minstep']; } if (view.config['shortcuts.maxstep']) { config['maxstep'] = view.config['shortcuts.maxstep']; } if (view.config['shortcuts.slowmotion']) { config['slowmotion'] = view.config['shortcuts.slowmotion']; } if (view.config['shortcuts.slowmotionvolume']) { config['slowmotionvolume'] = view.config['shortcuts.slowmotionvolume']; } // displayclick replacement if (config['displayclick']!='none') { displayclickButton.width = stage.stageWidth; displayclickButton.height = stage.stageHeight; displayclickButton.addEventListener(MouseEvent.CLICK, displayclickButtonHandler); } // listeners view.addModelListener(ModelEvent.TIME, timeHandler); view.addViewListener(ViewEvent.PLAY, playHandler); if (config['step']<0) { view.addModelListener(ModelEvent.META, metaHandler); } else { stepsize = config['step']; } stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler); stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler); iconsEnabled = view.config['icons']; }; /** Displayclick handler **/ function displayclickButtonHandler(e:MouseEvent):void { if (slowmoOn) { doSlowmo(); } switch (config['displayclick']) { case 'play': view.sendEvent(ViewEvent.PLAY); break; case 'fullscreen': view.sendEvent(ViewEvent.FULLSCREEN); break; } } /** Keyboard handlers **/ function keyUpHandler(e:KeyboardEvent):void { if (e.keyCode == Keyboard.SHIFT) { keyShift = false; } else if (e.keyCode == Keyboard.CONTROL) { keyCtrl = false; } } function keyDownHandler(e:KeyboardEvent):void { // clip.field.text = "k:"+e.keyCode+" c:"+e.charCode; if (slowmoOn) { doSlowmo(); // turn off slowmo } // Shift if (e.keyCode == Keyboard.SHIFT) { keyShift = true; } // Ctrl else if (e.keyCode == Keyboard.CONTROL) { keyCtrl = true; } // Space else if (e.keyCode==Keyboard.SPACE) { view.sendEvent(ViewEvent.PLAY); } // Right else if (e.keyCode == Keyboard.RIGHT && !seekInAction) { if (keyShift && keyCtrl) { doSeek(Math.round(stepsize/4)); } else if (keyShift) { doSeek(stepsize*2); } else if (keyCtrl) { doSeek(stepsize*4); } else { doSeek(stepsize); } } // Left else if (e.keyCode == Keyboard.LEFT && !seekInAction) { if (keyShift && keyCtrl) { doSeek( 0-Math.round(stepsize/4) ); } else if (keyShift) { doSeek( 0-(stepsize*2) ); } else if (keyCtrl) { doSeek( 0-(stepsize*4) ); } else { doSeek( 0-stepsize ); } } // . else if (e.charCode == 46) { doStep(); } // : else if (e.charCode == 58) { doSlowmo(); } // R else if (e.charCode == 82 || e.charCode == 114) { view.sendEvent(ViewEvent.SEEK, 0); } // Up else if (e.keyCode == Keyboard.UP) { doVolume(10); } // Down else if (e.keyCode == Keyboard.DOWN) { doVolume(-10); } // M else if (e.charCode == 77 || e.charCode == 109) { view.sendEvent(ViewEvent.MUTE); } // F else if (e.charCode == 70 || e.charCode == 102 || e.keyCode == Keyboard.ESCAPE) { view.sendEvent(ViewEvent.FULLSCREEN); } // N > else if (e.charCode == 78 || e.charCode == 110 || e.charCode == 62) { view.sendEvent(ViewEvent.NEXT); } // < P else if (e.charCode == 80 || e.charCode == 112 || e.charCode == 60) { view.sendEvent(ViewEvent.PREV); } // I else if (e.charCode == 73 || e.charCode == 105) { iconsEnabled = !iconsEnabled; view.config['icons'] = iconsEnabled; } } /** Volume **/ function doVolume(step:Number) { var v = view.config['volume']; v+=step; if (v<0) { v=0 } if (v>100) { v=100 } view.sendEvent(ViewEvent.VOLUME, v); } /** Slowmo **/ function doSlowmo():void { if (config['slowmotion']) { // off if (slowmoOn) { slowmoTimer.stop(); slowmoOn = false; view.sendEvent(ViewEvent.PLAY, 'false'); view.sendEvent(ViewEvent.VOLUME, slowmoVolume); view.config['icons'] = iconsEnabled; } // on else { // setup timer if not already done if (slowmoCount==0) { slowmoTimer = new Timer(slowmoDelay, 0); slowmoTimer.addEventListener(TimerEvent.TIMER, slowmoHandler); } // slowmoTimer.start(); slowmoProg = 0; slowmoOn = true; slowmoVolume = view.config['volume']; view.sendEvent(ViewEvent.VOLUME, config['slowmotionvolume']); view.config['icons'] = false; slowmoCount++; } } } function slowmoHandler(e:TimerEvent):void { if (slowmoProg==0) { view.sendEvent(ViewEvent.PLAY, 'true'); slowmoPlaying = true; } else if (slowmoProg==1) { view.sendEvent(ViewEvent.PLAY, 'false'); slowmoPlaying = false; } else if (slowmoProg==2) { slowmoProg=-1; } slowmoProg++; } /** Step **/ function doStep():void { // Send PLAY if not already playing if (config['state'] != 'PLAYING') { view.sendEvent(ViewEvent.PLAY, 'true'); } // Get timeHandler to issue a PAUSE waitingForPause = true; view.config['icons'] = false; } /** Seek **/ function doSeek(step:Number):void { seekPrePosition = position; // save current position var newpos = position+step; // new position if (newpos<0) { newpos = 0 } if (step<0 || positionconfig['maxstep']) { stepsize = config['maxstep']; } } } } /** Play handler **/ function playHandler(e:ViewEvent) { if (!slowmoOn && !seekInAction) { view.config['icons'] = iconsEnabled; } } /** Time handler **/ function timeHandler(e:ModelEvent) { position = e.data.position; duration = e.data.duration; if (waitingForPause) { // some action is wayting for a pause view.sendEvent(ViewEvent.PLAY, "false"); waitingForPause = false; } //clip.field.text = position; // if seeking if (seekInAction) { // at beginning already? if (seekTriedStep<0 && position<0.2) { seekInAction = false; } // no change? hasn't the current position changed more than a nudge? else if (position>seekPrePosition-1 && position0) { step = seekTriedStep+5; } else { step = seekTriedStep-5; } doSeek(step); } // done seeking else { seekInAction = false; } // pause if done seeking if (!seekInAction) { view.sendEvent(ViewEvent.PLAY, "false"); } } } }; }