'From Squeak3.5 of ''11 April 2003'' [latest update: #5180] on 8 March 2004 at 11:08:25 pm'! "Change Set: MPEGPlayer-Subtitles-dgd-asm Date: 8 March 2004 Author: Alejandro Magistrello & Diego Gomez Deck This changeset includes a lot of fixes/improvements to MPEGPlayer where the most important are the support for subtitles files (format .sub) and the full-screen option. List of changes: - support for subtitles of .sub format (see comment in class MPEGSubtitles) - full-screen mode - the quit button is always present - eToys vocabulary for #videoFileName and #subtitlesFileName. Using this new tiles with scripts in projects (or un book pages) in 'opening' mode we can create presentation with video in a multiplatform-way. - yellowbutton menu (usefull in full screen) - keyboard controls (usefull in full screen) - new UpdatingTextMorph class used to display the subtitles IMPORTANT: The instances of MPEGPlayer created before this changeset _will_ work including all the new stuff with the exception of the button-row that _will not_ get updated. Thanks to Alejandro Magistrello for the first version of this work!! "! Morph subclass: #MPEGDisplayMorph instanceVariableNames: 'frameBuffer mpegFile running desiredFrameRate allowFrameDropping repeat soundTrack volume startMSecs startFrame subtitles fullScreen subtitlesDisplayer ' classVariableNames: '' poolDictionaries: '' category: 'Movies-Player'! Object subclass: #MPEGSubtitleElement instanceVariableNames: 'initialFrame endFrame subtitleLine contents ' classVariableNames: '' poolDictionaries: '' category: 'Movies-Kernel'! !MPEGSubtitleElement commentStamp: 'asm 7/31/2003 22:27' prior: 0! an element of a subtitle file, this has the form {initialFrame}{endFrame} subtitle line[| next subtitle line]! Smalltalk renameClassNamed: #MPEGSubtitleFile as: #MPEGSubtitles! Object subclass: #MPEGSubtitles instanceVariableNames: 'fileName elements ' classVariableNames: '' poolDictionaries: '' category: 'Movies-Kernel'! !MPEGSubtitles commentStamp: 'asm 7/31/2003 22:12' prior: 0! a subtitle file i can only read subtitle files with a format like this: [..] {1043}{1082}La gente siempre me pregunta|si conozco a Tyler Durden. {1083}{1096}Tres minutos. {1097}{1133}El momento de la verdad.|Punto cero. [..] from Fight Club while reading, pipes(|) are replaced by carriage returns ! TextMorph subclass: #UpdatingTextMorph instanceVariableNames: 'target getSelector growable stepTime ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Widgets'! !UpdatingTextMorph commentStamp: 'asm 7/31/2003 21:27' prior: 0! A TextMorph that constantly tries to show the current data from the target object. When sent #step, it shows what the target objects has (target perform: getSelector).! UpdatingTextMorph subclass: #MPEGSubtitlesDisplayer instanceVariableNames: 'font ' classVariableNames: '' poolDictionaries: '' category: 'Movies-Player'! !MPEGDisplayMorph methodsFor: 'accessing' stamp: 'dgd 3/8/2004 18:57'! fullFileName "answer the receiver's fullFileName" ^ mpegFile isNil ifTrue: [''] ifFalse: [mpegFile fileName]! ! !MPEGDisplayMorph methodsFor: 'accessing' stamp: 'dgd 3/7/2004 18:52'! fullScreen "answer whatever the receiver is fullScreen Note: comparation with true to make it work with instances created before the introduccion of the variable" ^ fullScreen == true! ! !MPEGDisplayMorph methodsFor: 'accessing' stamp: 'dgd 3/7/2004 18:56'! fullScreen: aBoolean "change the receiver's fullScreen" fullScreen := aBoolean! ! !MPEGDisplayMorph methodsFor: 'accessing' stamp: 'dgd 3/8/2004 22:13'! subtitle "answer the subtitle for the current frame" self hasSubtitles ifFalse: [^ '']. self mpegFileIsOpen ifFalse: [^ '']. mpegFile hasVideo ifFalse:[^'']. "" ^ subtitles subtitleForFrame: (mpegFile videoGetFrame: 0)! ! !MPEGDisplayMorph methodsFor: 'accessing' stamp: 'dgd 3/8/2004 22:12'! subtitlesFileShortName "answer the receiver's subtitlesFileShortName" | fileFull defaultDirFull fileShort | self hasSubtitles ifFalse:[^ '']. " answer the shortest path to the file to make easier to move morphs with references to files between different platforms" fileFull := subtitles fileName. "" defaultDirFull := FileDirectory default fullName. fileShort := (fileFull beginsWith: defaultDirFull) ifTrue: [fileFull allButFirst: defaultDirFull size + 1] ifFalse: [fileFull]. "" ^ fileShort! ! !MPEGDisplayMorph methodsFor: 'accessing' stamp: 'dgd 3/8/2004 22:44'! subtitlesFileShortName: aString "change the receiver's subtitlesFileShortName, that means open the subtitles file named aString" | fullName | self mpegFileIsOpen ifFalse: [^ self]. mpegFile hasVideo ifFalse: [^ self]. "" fullName := FileDirectory default fullNameFor: aString. self openSubtitlesFileNamed: fullName! ! !MPEGDisplayMorph methodsFor: 'accessing' stamp: 'dgd 3/8/2004 21:47'! videoFileShortName "answer the receiver's videoFileShortName" | fileFull defaultDirFull fileShort | mpegFile isNil ifTrue: [^ '']. " answer the shortest path to the file to make easier to move morphs with references to files between different platforms" fileFull := mpegFile fileName. "" defaultDirFull := FileDirectory default fullName. fileShort := (fileFull beginsWith: defaultDirFull) ifTrue: [fileFull allButFirst: defaultDirFull size + 1] ifFalse: [fileFull]. "" ^ fileShort! ! !MPEGDisplayMorph methodsFor: 'accessing' stamp: 'dgd 3/8/2004 22:45'! videoFileShortName: aString "change the receiver's videoFileShortName, that means open the video file named aString" | fullName | self stopPlaying. fullName := FileDirectory default fullNameFor: aString. self openFileNamed: fullName! ! !MPEGDisplayMorph methodsFor: 'event handling' stamp: 'dgd 3/7/2004 20:06'! handlesKeyboard: evt ^ true! ! !MPEGDisplayMorph methodsFor: 'event handling' stamp: 'dgd 3/7/2004 23:02'! handlesMouseDown: evt ^ evt yellowButtonPressed! ! !MPEGDisplayMorph methodsFor: 'event handling' stamp: 'dgd 3/7/2004 22:37'! keyStroke: evt | char asc | char := evt keyCharacter. asc := char asciiValue. (char = $o or:[ char = $O]) ifTrue: ["open o/O" self openMPEGFile. ^self]. (char = $m or:[ char = $M]) ifTrue: ["menu key m/M" self invokeMenu. ^self]. (char = $r or:[ char = $R]) ifTrue: ["rewind r/R" self rewindMovie. ^self]. (char = $p or:[ char = $P]) ifTrue: ["play p/P" self startPlaying. ^self]. (char = $s or:[ char = $S]) ifTrue: ["stop s/S" self stopPlaying. ^self]. (asc = 28) ifTrue: [ "left arrow key" self previousFrame. ^self]. (asc = 29) ifTrue: [ "right arrow key" self nextFrame. ^self]. (char = $u or:[ char = $U]) ifTrue: ["subtitles file u/U" self openSubtitlesFile. ^self].! ! !MPEGDisplayMorph methodsFor: 'event handling' stamp: 'dgd 3/7/2004 22:13'! mouseDown: evt evt yellowButtonPressed ifTrue: [^ self invokeMenu]. super mouseDown: evt! ! !MPEGDisplayMorph methodsFor: 'file open/close' stamp: 'dgd 3/8/2004 22:09'! closeFile "Close my MPEG file, if any." mpegFile isNil ifFalse: [ mpegFile closeFile. mpegFile := nil. frameBuffer := nil]. subtitles := nil. self changed. ! ! !MPEGDisplayMorph methodsFor: 'file open/close' stamp: 'dgd 3/7/2004 19:07'! openFileNamed: mpegFileName "Try to open the MPEG file with the given name. Answer true if successful." | e | self closeFile. (FileDirectory default fileExists: mpegFileName) ifFalse: [self inform: ('File not found: {1}' translated format: {mpegFileName}). ^ false]. (MPEGFile isFileValidMPEG: mpegFileName) ifTrue: [mpegFile := MPEGFile openFile: mpegFileName] ifFalse: [ (JPEGMovieFile isJPEGMovieFile: mpegFileName) ifTrue: [mpegFile := JPEGMovieFile new openFileNamed: mpegFileName] ifFalse: [self inform: ('Not an MPEG or JPEG movie file: {1}' translated format: {mpegFileName}). ^ false]]. mpegFile fileHandle ifNil: [^ false]. "initialize soundTrack" mpegFile hasAudio ifTrue: [soundTrack := mpegFile audioPlayerForChannel: 1] ifFalse: [soundTrack := nil]. mpegFile hasVideo ifTrue: [ "set screen size and display first frame" desiredFrameRate := mpegFile videoFrameRate: 0. soundTrack ifNotNil: [ "compute frame rate from length of audio track" desiredFrameRate := (mpegFile videoFrames: 0) / soundTrack duration]. e := (mpegFile videoFrameWidth: 0)@(mpegFile videoFrameHeight: 0). frameBuffer := Form extent: e depth: (Display depth max: 16). super extent: e. self nextFrame] ifFalse: [ "hide screen for audio-only files" super extent: 250@0]. ! ! !MPEGDisplayMorph methodsFor: 'file open/close' stamp: 'dgd 3/8/2004 20:16'! openSubtitlesFile "Invoked by the 'Subtitles' button. Prompt for a file name and try to open that file as a subs file." | result | self mpegFileIsOpen ifFalse: [^ self]. mpegFile hasVideo ifFalse: [self inform: 'select a video file' translated. ^ self]. result := FileList2 modalFileSelectorForSuffixes: #('sub' ). result ifNil: [^ self]. self openSubtitlesFileNamed: result fullName! ! !MPEGDisplayMorph methodsFor: 'file open/close' stamp: 'dgd 3/8/2004 22:58'! openSubtitlesFileNamed: aString "Try to open the subtitle file with the given name. Answer true if successful." subtitles := nil. "" "try to create the displayer. it's useful for instances of mpegplayer older than the subtitles support" self subtitlesDisplayer. "" (FileDirectory default fileExists: aString) ifFalse: [self inform: ('File not found: {1}' translated format: {aString}). ^ false]. Utilities informUser: 'opening the file, please wait' translated during: [subtitles := MPEGSubtitles fromFileNamed: aString]! ! !MPEGDisplayMorph methodsFor: 'initialization' stamp: 'dgd 3/8/2004 23:05'! initialize "initialize the state of the receiver" super initialize."" super extent: 250 @ 0. frameBuffer := nil. mpegFile := nil. running := false. desiredFrameRate := 10.0. allowFrameDropping := true. repeat := false. soundTrack := nil. volume := 0.5. fullScreen := false. "" self initializeSubtitlesDisplayer! ! !MPEGDisplayMorph methodsFor: 'menu' stamp: 'dgd 3/8/2004 13:34'! doubleSize "change the receiver's extent to double of the normal size" self magnifyBy: 2! ! !MPEGDisplayMorph methodsFor: 'menu' stamp: 'dgd 3/8/2004 13:34'! halfSize "change the receiver's extent to a half of the normal size" self magnifyBy: 1 / 2! ! !MPEGDisplayMorph methodsFor: 'menu' stamp: 'dgd 3/8/2004 21:05'! invokeMenu "Invoke a menu of additonal functions." | aMenu | aMenu := MenuMorph new. aMenu defaultTarget: self. aMenu addList: { {'open file (o)' translated. #openMPEGFile}. #-. {'rewind (r)' translated. #rewindMovie}. {'play (p)' translated. #startPlaying}. {'stop (s)' translated. #stopPlaying}. {'previous frame (<-)' translated. #previousFrame}. {'next frame (->)' translated. #nextFrame}. #-. }. aMenu addLine. aMenu add: 'zoom' translated subMenu: self zoomSubMenu. aMenu add: 'subtitles' translated subMenu: self subtitlesSubMenu. aMenu add: 'advanced' translated subMenu: self advancedSubMenu. aMenu popUpEvent: self world activeHand lastEvent in: self world ! ! !MPEGDisplayMorph methodsFor: 'menu' stamp: 'dgd 3/8/2004 13:34'! normalSize "change the receiver's extent to the normal size" self magnifyBy: 1! ! !MPEGDisplayMorph methodsFor: 'menu' stamp: 'dgd 3/8/2004 22:57'! setSubtitlesBackgroundColor "open a dialog to change the background color of the subtitles" self subtitlesDisplayer openAPropertySheet! ! !MPEGDisplayMorph methodsFor: 'menu' stamp: 'dgd 3/8/2004 22:57'! setSubtitlesColor "open a dialog to change the color of the subtitles" self subtitlesDisplayer changeSubtitlesColor! ! !MPEGDisplayMorph methodsFor: 'menu' stamp: 'dgd 3/8/2004 22:57'! setSubtitlesFont "change the subtitles font" self subtitlesDisplayer changeFont! ! !MPEGDisplayMorph methodsFor: 'menu' stamp: 'dgd 3/8/2004 14:13'! toggleFullScreen "Toggle the fullScreen flag." mpegFile isNil ifTrue: [^ self]. mpegFile hasVideo ifFalse: [^ self]. "" self fullScreen: self fullScreen not. "" "set screen size" self fullScreen ifTrue: ["" self extent: Display extent. World activeHand newMouseFocus: self. self comeToFront] ifFalse: [self extent: self normalExtent]. "" (self fullScreen and: [self owner isKindOf: MPEGMoviePlayerMorph]) ifTrue: [self owner position: -6 @ -6] ifFalse: [self owner == self world ifFalse: [self owner position: 0 @ 0] ifTrue:[self position:0@0]]. "" self nextFrame! ! !MPEGDisplayMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 12:46'! advancedSubMenu "private - create the advanced submenu" | subMenu | subMenu := MenuMorph new. subMenu defaultTarget: self. repeat ifTrue: [subMenu add: 'turn off repeat (now on)' translated action: #toggleRepeat] ifFalse: [subMenu add: 'turn on repeat (now off)' translated action: #toggleRepeat]. subMenu addLine. subMenu addList: { {'set frame rate' translated. #setFrameRate}. #-. {'create JPEG movie from MPEG' translated. #createJPEGfromMPEG}. {'create JPEG movie from SqueakMovie' translated. #createJPEGfromSqueakMovie}. {'create JPEG movie from folder of frames' translated. #createJPEGfromFolderOfFrames} }. (mpegFile isKindOf: JPEGMovieFile) ifTrue: [ subMenu addLine. mpegFile hasAudio ifTrue: [subMenu add: 'remove all soundtracks' translated action: #removeAllSoundtracks] ifFalse: [subMenu add: 'add soundtrack' translated action: #addSoundtrack]]. ^ subMenu ! ! !MPEGDisplayMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 22:11'! hasSubtitles "answer if the receiver has subtitles or not" ^ mpegFile isNil not and: [subtitles isNil not]! ! !MPEGDisplayMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 23:03'! initializeSubtitlesDisplayer "private - builds the subtitle displayer" subtitlesDisplayer := MPEGSubtitlesDisplayer on: self selector: #subtitle. subtitlesDisplayer contents:''. self addMorphFront: subtitlesDisplayer. ^ subtitlesDisplayer! ! !MPEGDisplayMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 14:12'! magnifyBy: aNumber "private - scale the video (if any) to a scale of the normalExtent" | ne | fullScreen := false."" ne := self normalExtent. ne isNil ifFalse: [self extent: (ne * aNumber) rounded]! ! !MPEGDisplayMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 13:30'! normalExtent "private - answer the extent of the video, if any" (mpegFile isNil or: [mpegFile hasVideo not]) ifTrue: [^ nil]. "" ^ (mpegFile videoFrameWidth: 0) @ (mpegFile videoFrameHeight: 0)! ! !MPEGDisplayMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 22:59'! subtitlesDisplayer "private - answer the receiver's subtitlesDisplayer. create one if needed" ^ subtitlesDisplayer ifNil: [self initializeSubtitlesDisplayer]! ! !MPEGDisplayMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 22:22'! subtitlesSubMenu "private - create the subtitles submenu" | subMenu | subMenu := MenuMorph new. subMenu defaultTarget: self. subMenu add: 'open subtitles file (u)' translated action: #openSubtitlesFile. self hasSubtitles ifTrue: [ subMenu addLine. subMenu add: 'set subtitles font' translated action: #setSubtitlesFont. subMenu add: 'set subtitles color' translated action: #setSubtitlesColor. subMenu add: 'set subtitles background color' translated action: #setSubtitlesBackgroundColor]. ^ subMenu ! ! !MPEGDisplayMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 21:08'! zoomSubMenu "private - create the zoom submenu" | subMenu | subMenu := MenuMorph new. subMenu defaultTarget: self. self fullScreen ifTrue: [subMenu add: 'turn off full screen' translated action: #toggleFullScreen] ifFalse: [subMenu add: 'turn on full screen' translated action: #toggleFullScreen]. subMenu addLine. subMenu add: '50%' action: #halfSize. subMenu add: '100%' action: #normalSize. subMenu add: '200%' action: #doubleSize. ^ subMenu ! ! !MPEGMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'dgd 3/8/2004 21:42'! getSubtitlesFileName "answer the receiver's subtitlesFileName" ^ moviePlayer subtitlesFileShortName! ! !MPEGMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'dgd 3/8/2004 21:41'! getVideoFileName "answer the receiver's videoFileName" ^ moviePlayer videoFileShortName! ! !MPEGMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'dgd 3/8/2004 21:42'! setSubtitlesFileName: aString "change the subtitlesFileName" moviePlayer subtitlesFileShortName: aString! ! !MPEGMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'dgd 3/8/2004 21:41'! setVideoFileName: aString "change the videoFileName" moviePlayer videoFileShortName: aString! ! !MPEGMoviePlayerMorph methodsFor: 'event handling' stamp: 'dgd 3/7/2004 22:34'! handlesKeyboard: evt ^ moviePlayer handlesKeyboard: evt! ! !MPEGMoviePlayerMorph methodsFor: 'event handling' stamp: 'dgd 3/7/2004 22:34'! keyStroke: evt moviePlayer keyStroke: evt ! ! !MPEGMoviePlayerMorph methodsFor: 'submorphs-add/remove' stamp: 'dgd 3/8/2004 20:40'! delete "the receiver is being deleted" moviePlayer stopPlaying. moviePlayer closeFile. "" super delete! ! !MPEGMoviePlayerMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 20:40'! addButtonRow "private - add the button row" | r | r _ AlignmentMorph newRow vResizing: #shrinkWrap; color: Color transparent; listCentering: #center. r addMorphBack: (self buttonName: 'Menu' translated action: #invokeMenu). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Open' translated action: #openMPEGFile). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Rewind' translated action: #rewindMovie). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Play' translated action: #startPlaying). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Stop' translated action: #stopPlaying). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). " r addMorphBack: (self buttonName: '<' action: #previousFrame). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: '>' action: #nextFrame). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Subtitles' translated action: #openSubtitlesFile). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). " r addMorphBack: (self buildQuitButton). self addMorphBack: r. ! ! !MPEGMoviePlayerMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 20:40'! addPositionSlider "private - add the position slider" | r | positionSlider _ SimpleSliderMorph new color: (Color r: 0.71 g: 0.871 b: 1.0); extent: 200@2; target: moviePlayer; actionSelector: #moviePosition:; adjustToValue: 0. r _ AlignmentMorph newRow color: Color transparent; layoutInset: 0; wrapCentering: #center; cellPositioning: #leftCenter; listCentering: #center; hResizing: #shrinkWrap; vResizing: #rigid; height: 24. r addMorphBack: (StringMorph contents: 'start ' translated). r addMorphBack: positionSlider. r addMorphBack: (StringMorph contents: ' end' translated). self addMorphBack: r. ! ! !MPEGMoviePlayerMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 20:40'! addVolumeSlider "private - add the volume slider" | r | volumeSlider _ SimpleSliderMorph new color: (Color r: 0.71 g: 0.871 b: 1.0); extent: 200@2; target: moviePlayer; actionSelector: #volume:; adjustToValue: 0.5. r _ AlignmentMorph newRow color: Color transparent; layoutInset: 0; wrapCentering: #center; cellPositioning: #leftCenter; listCentering: #center; hResizing: #shrinkWrap; vResizing: #rigid; height: 24. r addMorphBack: (StringMorph contents: ' soft ' translated). r addMorphBack: volumeSlider. r addMorphBack: (StringMorph contents: ' loud' translated). self addMorphBack: r. ! ! !MPEGMoviePlayerMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 20:41'! buildQuitButton "private - create the [quit] button" ^ self buttonName: 'Quit' translated target: self action: #quit! ! !MPEGMoviePlayerMorph methodsFor: 'private' stamp: 'dgd 3/7/2004 19:22'! buttonName: aString action: aSymbol ^ self buttonName: aString target: moviePlayer action: aSymbol ! ! !MPEGMoviePlayerMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 20:41'! buttonName: aString target: anObject action: selector "private - create a button" ^ SimpleButtonMorph new target: anObject; label: aString; actionSelector: selector; color: (Color gray: 0.8); "old color" fillStyle: self buttonFillStyle; borderWidth: 0; borderColor: #raised. ! ! !MPEGMoviePlayerMorph methodsFor: 'private' stamp: 'dgd 3/8/2004 20:39'! quit "quit the receiver" self delete! ! !MPEGMoviePlayerMorph class methodsFor: '*Tools-FileList-registering' stamp: 'dgd 3/8/2004 20:37'! openOn: fileNameString "open a new instance of the receiver on a file named fileNameString " | wrapper | wrapper := self new. wrapper moviePlayer openFileNamed: fileNameString. ^ wrapper! ! !MPEGMoviePlayerMorph class methodsFor: 'scripting' stamp: 'dgd 3/8/2004 21:44'! additionsToViewerCategories "Answer a list of ( ) pairs that characterize the phrases this kind of morph wishes to add to various Viewer categories." ^ #( (playing ( (slot videoFileName 'The name for the video file' String readWrite Player getVideoFileName Player setVideoFileName:) (slot subtitlesFileName 'The name for the subtitles file' String readWrite Player getSubtitlesFileName Player setSubtitlesFileName:) (slot position 'A number representing the current position of the movie/sound.' Number readWrite Player getPosition Player setPosition:) (slot volume 'A number representing the volume of the movie.' Number readWrite Player getVolume Player setVolume:) (command play 'Start playing the movie/sound') (command stop 'Stop playing the movie/sound') (command rewind 'Rewind the movie/sound') (slot isRunning 'Whether the movie/sound is being played' Boolean readOnly Player getIsRunning unused unused) (slot repeat 'Whether the movie/sound will play in an endless loop' Boolean readWrite Player getRepeat Player setRepeat:) ) ))! ! !MPEGSubtitleElement methodsFor: 'parsing' stamp: 'asm 7/30/2003 21:04'! is: text in: aStream " Returns true if text is present in aStream. Advance the stream if present. " | position | (text isKindOf: Character) ifTrue: [ ^self is: (String with: text) in: aStream ]. position := aStream position. aStream skipSeparators. text = (aStream next: text size) ifFalse: [ aStream position: position. ^false ]. ^true! ! !MPEGSubtitleElement methodsFor: 'parsing' stamp: 'asm 7/30/2003 21:01'! mustBe: text in: aStream " Check text to be present in aStream. " (text isKindOf: Character) ifTrue: [ ^self is: (String with: text) in: aStream ]. (self is: text in: aStream) ifFalse: [ ^self error: 'Invalid token, must be: ',text ].! ! !MPEGSubtitleElement methodsFor: 'parsing' stamp: 'asm 7/30/2003 21:05'! nextIntegerFrom: aStream " Returns the next Integer present in aStream. " | sign result | sign := (self is: $- in: aStream) ifTrue: [-1] ifFalse: [1]. result := 0. self skipBlanks: aStream. [aStream peek isDigit] whileTrue: [ result := aStream next asciiValue - $0 asciiValue + (result * 10) ]. ^result * sign! ! !MPEGSubtitleElement methodsFor: 'parsing' stamp: 'dgd 3/8/2004 20:17'! readFrom: aStream "Private - Read the receiver's contents from aStream." self mustBe: '{' in: aStream. initialFrame := self nextIntegerFrom: aStream. self mustBe: '}{' in: aStream. endFrame := self nextIntegerFrom: aStream. self mustBe: '}' in: aStream. "" self contents: aStream nextLine isoToSqueak! ! !MPEGSubtitleElement methodsFor: 'parsing' stamp: 'asm 7/30/2003 21:42'! skipBlanks: aStream " Advance aStream skipping all blank characters and comments. " aStream skipSeparators! ! !MPEGSubtitleElement methodsFor: 'printing' stamp: 'dgd 3/8/2004 20:50'! printOn: aStream "append to aStream a sequence of characters that identifies the receiver." aStream nextPutAll: '{'; nextPutAll: initialFrame asString; nextPutAll: '}{'; nextPutAll: endFrame asString; nextPutAll: '}'; nextPutAll: contents asString! ! !MPEGSubtitleElement methodsFor: 'accessing' stamp: 'dgd 3/8/2004 20:17'! contents "answer the receiver's contents" ^ contents! ! !MPEGSubtitleElement methodsFor: 'accessing' stamp: 'dgd 3/8/2004 20:17'! contents: aString "change the receiver's contents" contents := aString replaceAll: $| with: Character cr! ! !MPEGSubtitleElement methodsFor: 'testing' stamp: 'dgd 3/8/2004 20:23'! correspondsToFrame: aNumber "answer if the receiver corresponds to a given frame number" ^ aNumber between: initialFrame and: endFrame! ! !MPEGSubtitleElement class methodsFor: 'instance creation' stamp: 'asm 7/30/2003 21:26'! fromStream: aStream "Returns an instance of the receiver read from aStream." ^self new readFrom: aStream! ! !MPEGSubtitles methodsFor: 'accessing' stamp: 'dgd 3/8/2004 20:49'! elementCorrespondingToFrame: frameNumber "answer the element corresponding to frameNumber" ^ elements detect: [:each | each correspondsToFrame: frameNumber] ifNone: []! ! !MPEGSubtitles methodsFor: 'accessing' stamp: 'dgd 3/8/2004 22:45'! fileName "answer the receiver's fileName" ^ fileName! ! !MPEGSubtitles methodsFor: 'accessing' stamp: 'dgd 3/8/2004 20:42'! subtitleForFrame: frameNumber "answer the subtitle for the given frame number" | element | element := self elementCorrespondingToFrame: frameNumber. ^ element isNil ifTrue: [''] ifFalse: [element contents]! ! !MPEGSubtitles methodsFor: 'initialization' stamp: 'dgd 3/8/2004 22:24'! initializeFromFileNamed: aString "initialize the receiver from a file named aString" | file result | fileName := aString. elements := OrderedCollection new. "" file := CrLfFileStream readOnlyFileNamed: aString. [result := self readFrom: file] ensure: [file close]. ^ result! ! !MPEGSubtitles methodsFor: 'initialization' stamp: 'dgd 3/8/2004 22:04'! readFrom: aStream "private - Read the next definitions found in aStream onto the receiver" [aStream atEnd] whileFalse: [| element | element := MPEGSubtitleElement fromStream: aStream. elements add: element]! ! !MPEGSubtitles class methodsFor: 'instance creation' stamp: 'dgd 3/8/2004 22:02'! fromFileNamed: aString "Returns an instance of the receiver read from file named aString" ^self new initializeFromFileNamed: aString ! ! !Player methodsFor: 'slot getters/setters' stamp: 'dgd 3/8/2004 21:40'! getSubtitlesFileName "Answer the subtitlesFileName in my costume" ^ costume renderedMorph getSubtitlesFileName! ! !Player methodsFor: 'slot getters/setters' stamp: 'dgd 3/8/2004 18:26'! getVideoFileName "Answer the videoFileName in my costume" ^ costume renderedMorph getVideoFileName! ! !Player methodsFor: 'slot getters/setters' stamp: 'dgd 3/8/2004 21:40'! setSubtitlesFileName: aString "Set my costume's subtitlesFileName as indicated" costume renderedMorph setSubtitlesFileName: aString! ! !Player methodsFor: 'slot getters/setters' stamp: 'dgd 3/8/2004 18:24'! setVideoFileName: aString "Set my costume's videoFileName as indicated" costume renderedMorph setVideoFileName: aString! ! !UpdatingTextMorph methodsFor: 'accessing' stamp: 'dgd 3/7/2004 20:16'! getSelector "answer the receiver's getSelector" ^ getSelector! ! !UpdatingTextMorph methodsFor: 'accessing' stamp: 'dgd 3/7/2004 20:16'! getSelector: aSymbol "change the receiver's getSelector" getSelector := aSymbol! ! !UpdatingTextMorph methodsFor: 'accessing' stamp: 'dgd 3/7/2004 20:17'! target "answer the receiver's target" ^ target! ! !UpdatingTextMorph methodsFor: 'accessing' stamp: 'dgd 3/7/2004 20:17'! target: anObject "change the receiver's target" target := anObject! ! !UpdatingTextMorph methodsFor: 'initialization' stamp: 'dgd 3/7/2004 20:17'! initialize "Initialie the receiver to have default values in its instance variables" super initialize."" stepTime := 50! ! !UpdatingTextMorph methodsFor: 'stepping and presenter' stamp: 'dgd 3/8/2004 20:34'! step "update my contents" | newContents | super step. "" newContents := self contentsFromTarget. self visible: newContents isEmpty not. self contents: newContents! ! !UpdatingTextMorph methodsFor: 'stepping and presenter' stamp: 'dgd 3/7/2004 20:19'! stepTime "answer the desired time between steps in milliseconds." ^ stepTime ifNil: [50]! ! !UpdatingTextMorph methodsFor: 'stepping and presenter' stamp: 'dgd 3/7/2004 20:19'! stepTime: mSecsPerStep "change the receiver's stepTime" stepTime := mSecsPerStep rounded! ! !UpdatingTextMorph methodsFor: 'target access' stamp: 'dgd 3/8/2004 20:35'! contentsFromTarget "private - answer the contents from the receiver's target" (target isNil or: [getSelector isNil]) ifTrue: [^ self contents]. "" ^ target perform: getSelector! ! !MPEGSubtitlesDisplayer methodsFor: 'accessing' stamp: 'dgd 3/7/2004 21:18'! font "answer the receiver's font" ^ font ifNil: [TextStyle defaultFont] ! ! !MPEGSubtitlesDisplayer methodsFor: 'accessing' stamp: 'dgd 3/8/2004 20:33'! font: aFont "change the receiver's font" font := aFont. "" self contents: ''. self contents: self contentsFromTarget! ! !MPEGSubtitlesDisplayer methodsFor: 'initialization' stamp: 'dgd 3/7/2004 21:16'! initialize "initialiaze the receiver" super initialize. "" font := TextStyle defaultFont."" self backgroundColor: (Color black alpha: 0.4). "" self margins: 4 @ 2. self textColor: Color white. self textStyle centered! ! !MPEGSubtitlesDisplayer methodsFor: 'menu' stamp: 'dgd 3/8/2004 20:42'! changeFont "open a dialog to change the receiver's font" | newFont | newFont := StrikeFont fromUser: self font. "" newFont isNil ifFalse: [self font: newFont]! ! !MPEGSubtitlesDisplayer methodsFor: 'menu' stamp: 'dgd 3/8/2004 20:10'! changeSubtitlesColor "offer a ColorPicker to change the subtitles colors" ColorPickerMorph new choseModalityFromPreference; sourceHand: self activeHand; target: self; selector: #textColor:; originalColor: self textColor; putUpFor: self currentHand near: self currentHand cursorBounds ! ! !MPEGSubtitlesDisplayer methodsFor: 'stepping and presenter' stamp: 'dgd 3/7/2004 20:59'! step "update my position" super step. " if my owner is the mpegplayer, i change my position to bottomCenter" self owner == self target ifTrue: [| bc | bc := self owner bottomCenter. self left: bc x - (self width // 2). self bottom: bc y]! ! !MPEGSubtitlesDisplayer methodsFor: 'target access' stamp: 'dgd 3/8/2004 20:36'! contentsFromTarget "private - answer the contents from the receiver's target" | contentsAsText | contentsAsText := super contentsFromTarget asText. contentsAsText addAttribute: (TextFontReference toFont: self font). ^ contentsAsText! ! !UpdatingTextMorph class methodsFor: 'instance creation' stamp: 'dgd 3/8/2004 20:31'! on: targetObject selector: aSymbol "answer a new instance of the receiver on a given target and selector" ^ self new getSelector: aSymbol; target: targetObject! ! MPEGSubtitlesDisplayer removeSelector: #changeSubTitlesColor! MPEGSubtitlesDisplayer removeSelector: #readFromTarget! MPEGSubtitlesDisplayer removeSelector: #setSubTitlesColor! !MPEGSubtitlesDisplayer reorganize! ('accessing' font font:) ('initialization' initialize) ('menu' changeFont changeSubtitlesColor) ('stepping and presenter' step) ('target access' contentsFromTarget) ! UpdatingTextMorph removeSelector: #readFromTarget! MPEGSubtitles class removeSelector: #fromPathName:! MPEGSubtitles class removeSelector: #fromStream:! MPEGSubtitles class removeSelector: #new! MPEGSubtitles removeSelector: #elementContainig:! MPEGSubtitles removeSelector: #initialize! MPEGSubtitles removeSelector: #initializeSubtitleLines! MPEGSubtitles removeSelector: #nextSubtitleElementFrom:! MPEGSubtitles removeSelector: #nextSubtitleLineFrom:! MPEGSubtitles removeSelector: #readLinesFrom:! MPEGSubtitles removeSelector: #subtitleLineForFrame:! MPEGSubtitles removeSelector: #subtitleLines! !MPEGSubtitles reorganize! ('accessing' elementCorrespondingToFrame: fileName subtitleForFrame:) ('initialization' initializeFromFileNamed: readFrom:) ! MPEGSubtitleElement removeSelector: #endFrame! MPEGSubtitleElement removeSelector: #initialFrame! MPEGSubtitleElement removeSelector: #subtitleLine! MPEGSubtitleElement removeSelector: #subtitleLine:! MPEGMoviePlayerMorph removeSelector: #addQuitButton! MPEGMoviePlayerMorph removeSelector: #invokeMenu! !MPEGMoviePlayerMorph reorganize! ('*Tools-FileList-accessing' moviePlayer) ('accessing' getPosition getVolume guessVolumeSlider setPosition: setVolume: volumeSlider) ('drawing' drawOn:) ('e-toy support' defaultFloatPrecisionFor: getIsRunning getRepeat getSubtitlesFileName getVideoFileName play rewind setRepeat: setSubtitlesFileName: setVideoFileName: stop) ('event handling' handlesKeyboard: keyStroke:) ('initialization' initialize) ('stepping' step stepTime) ('submorphs-add/remove' delete) ('private' addButtonRow addPositionSlider addVolumeSlider buildQuitButton buttonFillStyle buttonName:action: buttonName:target:action: moviePlayerFillStyle quit) ! MPEGDisplayMorph removeSelector: #advancedSubmenu! MPEGDisplayMorph removeSelector: #buildSubtitlesDisplayer! MPEGDisplayMorph removeSelector: #closeSubtitlesFile! MPEGDisplayMorph removeSelector: #extentToScale:! MPEGDisplayMorph removeSelector: #fileName! MPEGDisplayMorph removeSelector: #guessSubtitlesDisplayer! MPEGDisplayMorph removeSelector: #openSubtitleFileNamed:! MPEGDisplayMorph removeSelector: #setSubTitlesBackgroundColor! MPEGDisplayMorph removeSelector: #setSubTitlesColor! MPEGDisplayMorph removeSelector: #setSubTitlesFont! MPEGDisplayMorph removeSelector: #shortFileName! MPEGDisplayMorph removeSelector: #shortFileName:! MPEGDisplayMorph removeSelector: #showMenu! MPEGDisplayMorph removeSelector: #subtitleLine! MPEGDisplayMorph removeSelector: #subtitlesSubmenu! !MPEGDisplayMorph reorganize! ('accessing' extent: fullFileName fullScreen fullScreen: isRunning moviePosition moviePosition: repeat repeat: subtitle subtitlesFileShortName subtitlesFileShortName: videoFileShortName videoFileShortName: volume volume:) ('commands' nextFrame previousFrame rewindMovie setFrameRate startPlaying stopPlaying) ('drawing' areasRemainingToFill: drawOn: drawScaledOn:) ('event handling' handlesKeyboard: handlesMouseDown: keyStroke: mouseDown:) ('file open/close' closeFile mpegFileIsOpen openFileNamed: openMPEGFile openSubtitlesFile openSubtitlesFileNamed:) ('initialization' initialize) ('menu' addSoundtrack createJPEGfromFolderOfFrames createJPEGfromMPEG createJPEGfromSqueakMovie doubleSize halfSize invokeMenu normalSize removeAllSoundtracks setSubtitlesBackgroundColor setSubtitlesColor setSubtitlesFont toggleFullScreen toggleRepeat) ('other' advanceFrame jpegMovieSize: measureMaxFrameRate) ('stepping' step stepTime) ('private' advancedSubMenu hasSubtitles initializeSubtitlesDisplayer magnifyBy: normalExtent subtitlesDisplayer subtitlesSubMenu zoomSubMenu) !