2020-07-08 13:27:41 -04:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
2021-12-26 18:47:58 +01:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2020-07-08 13:27:41 -04:00
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2021-12-26 18:47:58 +01:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2020-07-08 13:27:41 -04:00
*
*/
# include "director/director.h"
# include "director/movie.h"
# include "director/score.h"
2020-07-17 23:27:52 -04:00
# include "director/cursor.h"
2020-08-21 10:51:44 +02:00
# include "director/cast.h"
2020-07-08 13:27:41 -04:00
# include "director/channel.h"
# include "director/sprite.h"
# include "director/castmember.h"
2021-10-09 01:03:32 +08:00
# include "director/types.h"
2021-06-27 16:13:00 +08:00
# include "director/window.h"
2020-07-08 13:27:41 -04:00
# include "graphics/macgui/mactext.h"
2021-06-13 22:05:11 +08:00
# include "graphics/macgui/macbutton.h"
2020-07-08 13:27:41 -04:00
namespace Director {
2020-07-26 04:32:29 +00:00
Channel : : Channel ( Sprite * sp , int priority ) {
2021-06-21 15:58:40 +08:00
if ( ! sp )
_sprite = nullptr ;
else
_sprite = new Sprite ( * sp ) ;
2020-07-26 04:32:29 +00:00
_widget = nullptr ;
2021-11-06 16:35:12 +08:00
_currentPoint = _sprite ? _sprite - > _startPoint : Common : : Point ( 0 , 0 ) ;
2020-07-08 13:27:41 -04:00
_delta = Common : : Point ( 0 , 0 ) ;
_constraint = 0 ;
2020-07-26 04:32:29 +00:00
_mask = nullptr ;
2020-07-08 13:27:41 -04:00
2020-07-26 04:32:29 +00:00
_priority = priority ;
2021-11-06 16:35:12 +08:00
_width = _sprite ? _sprite - > _width : 0 ;
_height = _sprite ? _sprite - > _height : 0 ;
2020-07-09 19:06:30 -04:00
2020-08-18 23:37:46 +02:00
_movieRate = 0.0 ;
2020-08-01 23:14:37 +08:00
_movieTime = 0 ;
_startTime = 0 ;
_stopTime = 0 ;
2021-10-09 01:03:32 +08:00
_filmLoopFrame = 0 ;
2020-07-08 13:27:41 -04:00
_visible = true ;
_dirty = true ;
2022-03-21 17:26:05 +05:30
if ( _sprite )
_sprite - > updateEditable ( ) ;
2020-07-08 13:27:41 -04:00
}
2021-11-06 16:35:12 +08:00
Channel : : Channel ( const Channel & channel ) {
* this = channel ;
}
Channel & Channel : : operator = ( const Channel & channel ) {
_sprite = channel . _sprite ? new Sprite ( * channel . _sprite ) : nullptr ;
_widget = nullptr ;
_currentPoint = channel . _currentPoint ;
_delta = channel . _delta ;
_constraint = channel . _constraint ;
_mask = nullptr ;
_priority = channel . _priority ;
_width = channel . _width ;
_height = channel . _height ;
_movieRate = channel . _movieRate ;
_movieTime = channel . _movieTime ;
_startTime = channel . _startTime ;
_stopTime = channel . _stopTime ;
_filmLoopFrame = channel . _filmLoopFrame ;
_visible = channel . _visible ;
_dirty = channel . _dirty ;
return * this ;
}
2020-07-26 04:32:29 +00:00
Channel : : ~ Channel ( ) {
2021-11-06 16:35:12 +08:00
if ( _widget )
delete _widget ;
if ( _mask )
delete _mask ;
if ( _sprite )
delete _sprite ;
2020-07-26 04:32:29 +00:00
}
2020-07-09 11:08:15 -04:00
DirectorPlotData Channel : : getPlotData ( ) {
2021-10-09 01:03:32 +08:00
DirectorPlotData pd ( g_director - > _wm , _sprite - > _spriteType , _sprite - > _ink , _sprite - > _blend , _sprite - > getBackColor ( ) , _sprite - > getForeColor ( ) ) ;
2020-07-09 15:33:03 -04:00
pd . colorWhite = pd . _wm - > _colorWhite ;
pd . colorBlack = pd . _wm - > _colorBlack ;
2020-07-13 11:19:07 +02:00
pd . dst = nullptr ;
2020-07-09 11:08:15 -04:00
2020-07-09 15:33:03 -04:00
pd . srf = getSurface ( ) ;
2020-08-10 11:59:06 -04:00
if ( ! pd . srf & & _sprite - > _spriteType ! = kBitmapSprite ) {
2020-07-09 15:33:03 -04:00
// Shapes come colourized from macDrawPixel
2021-10-09 01:03:32 +08:00
pd . ms = _sprite - > getShape ( ) ;
2020-07-09 15:33:03 -04:00
pd . applyColor = false ;
2020-07-09 11:08:15 -04:00
} else {
2021-07-08 22:33:00 +08:00
pd . setApplyColor ( ) ;
2020-07-09 11:08:15 -04:00
}
return pd ;
}
2020-07-08 13:27:41 -04:00
Graphics : : ManagedSurface * Channel : : getSurface ( ) {
2021-07-17 14:24:42 +08:00
if ( _widget ) {
2020-07-26 04:32:29 +00:00
return _widget - > getSurface ( ) ;
2020-07-08 13:27:41 -04:00
} else {
return nullptr ;
}
}
const Graphics : : Surface * Channel : : getMask ( bool forceMatte ) {
2020-07-09 09:29:57 -04:00
if ( ! _sprite - > _cast | | _sprite - > _spriteType = = kTextSprite )
2020-07-08 13:27:41 -04:00
return nullptr ;
bool needsMatte = _sprite - > _ink = = kInkTypeMatte | |
_sprite - > _ink = = kInkTypeNotCopy | |
_sprite - > _ink = = kInkTypeNotTrans | |
_sprite - > _ink = = kInkTypeNotReverse | |
2020-07-29 00:11:28 -04:00
_sprite - > _ink = = kInkTypeNotGhost | |
2021-07-30 13:21:19 -04:00
_sprite - > _ink = = kInkTypeBlend | |
_sprite - > _ink = = kInkTypeAddPin | |
_sprite - > _ink = = kInkTypeAdd | |
_sprite - > _ink = = kInkTypeSubPin | |
_sprite - > _ink = = kInkTypeLight | |
_sprite - > _ink = = kInkTypeSub | |
_sprite - > _ink = = kInkTypeDark | |
2020-07-29 00:11:28 -04:00
_sprite - > _blend > 0 ;
2020-07-08 13:27:41 -04:00
2021-07-12 15:24:32 +08:00
Common : : Rect bbox ( getBbox ( ) ) ;
2020-07-08 13:27:41 -04:00
if ( needsMatte | | forceMatte ) {
// Mattes are only supported in bitmaps for now. Shapes don't need mattes,
// as they already have all non-enclosed white pixels transparent.
// Matte on text has a trivial enough effect to not worry about implementing.
if ( _sprite - > _cast - > _type = = kCastBitmap ) {
2021-07-12 15:24:32 +08:00
return ( ( BitmapCastMember * ) _sprite - > _cast ) - > getMatte ( bbox ) ;
2020-07-08 13:27:41 -04:00
} else {
return nullptr ;
}
} else if ( _sprite - > _ink = = kInkTypeMask ) {
2021-06-30 18:41:00 -04:00
CastMemberID maskID ( _sprite - > _castId . member + 1 , _sprite - > _castId . castLib ) ;
CastMember * member = g_director - > getCurrentMovie ( ) - > getCastMember ( maskID ) ;
2020-07-08 13:27:41 -04:00
if ( member & & member - > _initialRect = = _sprite - > _cast - > _initialRect ) {
2021-07-17 14:30:00 +08:00
Graphics : : MacWidget * widget = member - > createWidget ( bbox , this , _sprite - > _spriteType ) ;
2020-07-26 04:32:29 +00:00
if ( _mask )
delete _mask ;
_mask = new Graphics : : ManagedSurface ( ) ;
_mask - > copyFrom ( * widget - > getSurface ( ) ) ;
delete widget ;
return & _mask - > rawSurface ( ) ;
2020-07-08 13:27:41 -04:00
} else {
warning ( " Channel::getMask(): Requested cast mask, but no matching mask was found " ) ;
return nullptr ;
}
}
return nullptr ;
}
2021-06-22 21:52:14 +08:00
// TODO: eliminate this function when we got the correct method to deal with sprite size
// since we didn't handle sprites very well for text cast members. thus we don't replace our text castmembers when only size changes
// for explicitly changing, we have isModified to check
bool hasTextCastMember ( Sprite * sprite ) {
if ( sprite & & sprite - > _cast )
return sprite - > _cast - > _type = = kCastText | | sprite - > _cast - > _type = = kCastButton ;
return false ;
}
2020-07-08 13:27:41 -04:00
bool Channel : : isDirty ( Sprite * nextSprite ) {
// When a sprite is puppeted setTheSprite ensures that the dirty flag here is
// set. Otherwise, we need to rerender when the position, bounding box, or
// cast of the sprite changes.
2020-07-09 23:56:08 -04:00
if ( ! nextSprite )
return false ;
2021-03-28 02:33:53 +01:00
bool isDirtyFlag = _dirty | |
2020-07-08 13:27:41 -04:00
_delta ! = Common : : Point ( 0 , 0 ) | |
( _sprite - > _cast & & _sprite - > _cast - > isModified ( ) ) ;
2022-03-21 17:26:05 +05:30
if ( _sprite & & ! _sprite - > _puppet ) {
2020-07-21 20:19:32 -04:00
// When puppet is set, the overall dirty flag should be set when sprite is
// modified.
2021-03-28 02:33:53 +01:00
isDirtyFlag | = _sprite - > _castId ! = nextSprite - > _castId | |
2020-07-21 20:19:32 -04:00
_sprite - > _ink ! = nextSprite - > _ink ;
2020-07-09 23:56:08 -04:00
if ( ! _sprite - > _moveable )
2021-03-28 02:33:53 +01:00
isDirtyFlag | = _currentPoint ! = nextSprite - > _startPoint ;
2021-06-22 21:52:14 +08:00
if ( ! _sprite - > _stretch & & ! hasTextCastMember ( _sprite ) )
2021-03-28 02:33:53 +01:00
isDirtyFlag | = _width ! = nextSprite - > _width | | _height ! = nextSprite - > _height ;
2020-07-08 13:27:41 -04:00
}
2021-03-28 02:33:53 +01:00
return isDirtyFlag ;
2020-07-08 13:27:41 -04:00
}
2020-07-09 19:12:33 -04:00
bool Channel : : isStretched ( ) {
return _sprite - > _puppet & & _sprite - > _stretch & &
( _sprite - > _width ! = _width | | _sprite - > _height ! = _height ) ;
}
2020-07-14 09:28:19 -04:00
bool Channel : : isEmpty ( ) {
return ( _sprite - > _spriteType = = kInactiveSprite ) ;
}
2020-07-19 17:04:59 +02:00
bool Channel : : isActiveText ( ) {
if ( _sprite - > _spriteType ! = kTextSprite )
return false ;
2020-07-26 04:32:29 +00:00
if ( _widget & & _widget - > hasAllFocus ( ) )
2020-07-19 17:04:59 +02:00
return true ;
return false ;
}
2020-07-21 23:08:53 -04:00
bool Channel : : isMouseIn ( const Common : : Point & pos ) {
2021-08-23 15:59:02 -04:00
if ( ! _visible )
return false ;
2020-07-21 15:05:52 -04:00
2021-08-23 15:59:02 -04:00
Common : : Rect bbox = getBbox ( ) ;
2020-07-21 15:05:52 -04:00
if ( ! bbox . contains ( pos ) )
return false ;
2020-07-21 23:08:53 -04:00
if ( _sprite - > _ink = = kInkTypeMatte ) {
2020-07-21 16:21:56 -04:00
if ( _sprite - > _cast & & _sprite - > _cast - > _type = = kCastBitmap ) {
2021-07-12 15:24:32 +08:00
Graphics : : Surface * matte = ( ( BitmapCastMember * ) _sprite - > _cast ) - > getMatte ( bbox ) ;
2020-07-21 15:05:52 -04:00
return matte ? ! ( * ( byte * ) ( matte - > getBasePtr ( pos . x - bbox . left , pos . y - bbox . top ) ) ) : true ;
}
}
return true ;
}
2020-07-22 14:19:15 -04:00
bool Channel : : isMatteIntersect ( Channel * channel ) {
Common : : Rect myBbox = getBbox ( ) ;
Common : : Rect yourBbox = channel - > getBbox ( ) ;
Common : : Rect intersectRect = myBbox . findIntersectingRect ( yourBbox ) ;
2021-06-27 14:58:55 +08:00
if ( intersectRect . isEmpty ( ) )
2020-07-22 14:19:15 -04:00
return false ;
2021-06-27 13:35:35 +08:00
Graphics : : Surface * myMatte = nullptr ;
Graphics : : Surface * yourMatte = nullptr ;
if ( _sprite - > _cast & & _sprite - > _cast - > _type = = kCastBitmap )
2021-07-12 15:24:32 +08:00
myMatte = ( ( BitmapCastMember * ) _sprite - > _cast ) - > getMatte ( myBbox ) ;
2021-06-27 13:35:35 +08:00
if ( channel - > _sprite - > _cast & & channel - > _sprite - > _cast - > _type = = kCastBitmap )
2021-07-12 15:24:32 +08:00
yourMatte = ( ( BitmapCastMember * ) channel - > _sprite - > _cast ) - > getMatte ( yourBbox ) ;
2020-07-09 22:45:58 -04:00
2020-07-22 14:19:15 -04:00
if ( myMatte & & yourMatte ) {
for ( int i = intersectRect . top ; i < intersectRect . bottom ; i + + ) {
const byte * my = ( const byte * ) myMatte - > getBasePtr ( intersectRect . left - myBbox . left , i - myBbox . top ) ;
const byte * your = ( const byte * ) yourMatte - > getBasePtr ( intersectRect . left - yourBbox . left , i - yourBbox . top ) ;
for ( int j = intersectRect . left ; j < intersectRect . right ; j + + , my + + , your + + )
if ( ! * my & & ! * your )
return true ;
}
}
return false ;
}
2021-06-27 13:35:35 +08:00
// this contains channel. i.e. myBox contain yourBox
2020-07-22 14:19:15 -04:00
bool Channel : : isMatteWithin ( Channel * channel ) {
Common : : Rect myBbox = getBbox ( ) ;
Common : : Rect yourBbox = channel - > getBbox ( ) ;
Common : : Rect intersectRect = myBbox . findIntersectingRect ( yourBbox ) ;
2021-06-27 13:35:35 +08:00
if ( ! myBbox . contains ( yourBbox ) )
2020-07-22 14:19:15 -04:00
return false ;
2021-06-27 13:35:35 +08:00
Graphics : : Surface * myMatte = nullptr ;
Graphics : : Surface * yourMatte = nullptr ;
if ( _sprite - > _cast & & _sprite - > _cast - > _type = = kCastBitmap )
2021-07-12 15:24:32 +08:00
myMatte = ( ( BitmapCastMember * ) _sprite - > _cast ) - > getMatte ( myBbox ) ;
2021-06-27 13:35:35 +08:00
if ( channel - > _sprite - > _cast & & channel - > _sprite - > _cast - > _type = = kCastBitmap )
2021-07-12 15:24:32 +08:00
yourMatte = ( ( BitmapCastMember * ) channel - > _sprite - > _cast ) - > getMatte ( yourBbox ) ;
2020-07-22 14:19:15 -04:00
if ( myMatte & & yourMatte ) {
for ( int i = intersectRect . top ; i < intersectRect . bottom ; i + + ) {
const byte * my = ( const byte * ) myMatte - > getBasePtr ( intersectRect . left - myBbox . left , i - myBbox . top ) ;
const byte * your = ( const byte * ) yourMatte - > getBasePtr ( intersectRect . left - yourBbox . left , i - yourBbox . top ) ;
for ( int j = intersectRect . left ; j < intersectRect . right ; j + + , my + + , your + + )
if ( * my & & ! * your )
return false ;
}
return true ;
}
return false ;
}
2020-08-18 13:08:14 +02:00
bool Channel : : isActiveVideo ( ) {
2022-03-21 17:26:05 +05:30
if ( _sprite & & ( ! _sprite - > _cast | | _sprite - > _cast - > _type ! = kCastDigitalVideo ) )
2020-08-18 13:08:14 +02:00
return false ;
return true ;
}
2021-08-13 18:01:59 +08:00
void Channel : : updateVideoTime ( ) {
2022-03-21 17:26:05 +05:30
if ( _sprite )
_movieTime = ( ( DigitalVideoCastMember * ) _sprite - > _cast ) - > getMovieCurrentTime ( ) ;
2021-08-13 18:01:59 +08:00
}
2020-08-23 19:31:14 +02:00
bool Channel : : isVideoDirectToStage ( ) {
if ( ! _sprite - > _cast | | _sprite - > _cast - > _type ! = kCastDigitalVideo )
return false ;
return ( ( DigitalVideoCastMember * ) _sprite - > _cast ) - > _directToStage ;
}
2020-07-22 14:19:15 -04:00
Common : : Rect Channel : : getBbox ( bool unstretched ) {
2020-07-09 22:45:58 -04:00
Common : : Rect result ( unstretched ? _sprite - > _width : _width ,
unstretched ? _sprite - > _height : _height ) ;
2020-07-09 19:06:30 -04:00
result . moveTo ( getPosition ( ) ) ;
2020-07-08 13:27:41 -04:00
2020-07-09 19:06:30 -04:00
return result ;
2020-07-08 13:27:41 -04:00
}
2021-06-30 18:41:00 -04:00
void Channel : : setCast ( CastMemberID memberID ) {
2021-08-12 14:45:57 +08:00
// release previous widget
if ( _sprite - > _cast )
_sprite - > _cast - > releaseWidget ( ) ;
2021-07-08 10:10:07 +08:00
_sprite - > setCast ( memberID ) ;
2020-07-26 04:32:29 +00:00
_width = _sprite - > _width ;
_height = _sprite - > _height ;
2021-06-30 18:41:00 -04:00
replaceWidget ( ) ;
2020-07-26 04:32:29 +00:00
}
2020-07-16 18:19:25 -04:00
void Channel : : setClean ( Sprite * nextSprite , int spriteId , bool partial ) {
2020-07-08 13:27:41 -04:00
if ( ! nextSprite )
return ;
2021-06-30 18:41:00 -04:00
CastMemberID previousCastId ( 0 , 0 ) ;
2020-07-26 04:32:29 +00:00
bool replace = isDirty ( nextSprite ) ;
2020-07-08 13:27:41 -04:00
2021-07-03 11:03:12 +08:00
// for dirty situation that we need to replace widget.
// if cast are modified, then we need to replace it
// if cast size are changed, and we may need to replace it, because we may having the scaled bitmap castmember
// other situation, e.g. position changing, we will let channel to handle it. So we don't have to replace widget
bool dimsChanged = ! _sprite - > _stretch & & ! hasTextCastMember ( _sprite ) & & ( _sprite - > _width ! = nextSprite - > _width | | _sprite - > _height ! = nextSprite - > _height ) ;
2021-07-17 15:50:17 +08:00
// if spriteType is changing, then we may need to re-create the widget since spriteType will guide when we creating widget
bool spriteTypeChanged = _sprite - > _spriteType ! = nextSprite - > _spriteType ;
2020-07-16 18:19:25 -04:00
if ( nextSprite ) {
2020-08-22 17:06:39 +02:00
if ( nextSprite - > _cast & & ( _dirty | | _sprite - > _castId ! = nextSprite - > _castId ) ) {
2020-08-21 10:51:44 +02:00
if ( nextSprite - > _cast - > _type = = kCastDigitalVideo ) {
2021-06-30 18:41:00 -04:00
Common : : String path = nextSprite - > _cast - > getCast ( ) - > getVideoPath ( nextSprite - > _castId . member ) ;
2020-08-21 10:51:44 +02:00
if ( ! path . empty ( ) ) {
2021-07-23 22:55:41 -04:00
( ( DigitalVideoCastMember * ) nextSprite - > _cast ) - > loadVideo ( pathMakeRelative ( path , true , false ) ) ;
2020-08-21 10:51:44 +02:00
( ( DigitalVideoCastMember * ) nextSprite - > _cast ) - > startVideo ( this ) ;
}
2021-10-09 01:03:32 +08:00
} else if ( nextSprite - > _cast - > _type = = kCastFilmLoop ) {
// brand new film loop, reset the frame counter
_filmLoopFrame = 0 ;
}
}
// if the next sprite in the channel shares the cast member
if ( nextSprite - > _cast & & _sprite - > _castId = = nextSprite - > _castId ) {
if ( nextSprite - > _cast - > _type = = kCastFilmLoop ) {
// increment the film loop counter
_filmLoopFrame + = 1 ;
_filmLoopFrame % = ( ( FilmLoopCastMember * ) nextSprite - > _cast ) - > _frames . size ( ) ;
2020-08-21 10:51:44 +02:00
}
2020-08-20 23:20:46 +02:00
}
2021-07-21 20:43:03 +08:00
// for the non-puppet QDShape, since we won't use isDirty to check whether the QDShape is changed.
// so we may always keep the sprite info because we need it to draw QDShape.
if ( _sprite - > _puppet | | ( ! nextSprite - > isQDShape ( ) & & partial ) ) {
2020-07-27 16:28:47 -04:00
// Updating scripts, etc. does not require a full re-render
_sprite - > _scriptId = nextSprite - > _scriptId ;
} else {
2021-06-22 21:52:14 +08:00
previousCastId = _sprite - > _castId ;
2020-07-29 12:39:37 -04:00
replaceSprite ( nextSprite ) ;
2020-07-09 19:06:30 -04:00
}
2020-07-08 13:27:41 -04:00
2020-07-16 18:19:25 -04:00
_currentPoint + = _delta ;
_delta = Common : : Point ( 0 , 0 ) ;
}
2020-07-08 13:27:41 -04:00
2021-07-17 15:54:53 +08:00
// FIXME: organize the logic here.
// for the dirty puppet sprites, we will always replaceWidget since previousCastId is 0, but we shouldn't replace the widget of there are only position changing
// e.g. we won't want a puppet editable text sprite changing because that will cause the loss of text.
2020-07-26 04:32:29 +00:00
if ( replace ) {
2021-08-18 20:19:01 +08:00
_sprite - > updateEditable ( ) ;
2021-07-17 15:50:17 +08:00
replaceWidget ( previousCastId , dimsChanged | | spriteTypeChanged ) ;
2020-07-08 13:27:41 -04:00
}
2020-08-13 23:37:10 -04:00
2021-06-27 16:13:00 +08:00
updateTextCast ( ) ;
2021-06-13 22:05:11 +08:00
updateGlobalAttr ( ) ;
2020-08-13 23:37:10 -04:00
2021-08-05 16:48:19 +08:00
// reset the stop time when we are not playing video
2021-08-05 17:10:52 +08:00
if ( _stopTime & & ( ! _sprite - > _cast | | ( _sprite - > _cast & & _sprite - > _cast - > _type ! = kCastDigitalVideo ) ) )
2021-08-05 16:48:19 +08:00
_stopTime = 0 ;
2020-07-26 04:32:29 +00:00
_dirty = false ;
2020-07-08 13:27:41 -04:00
}
2021-06-27 16:13:00 +08:00
// this is used to for setting and updating text castmember
// e.g. set editable, update dims for auto expanding
void Channel : : updateTextCast ( ) {
if ( ! _sprite - > _cast | | _sprite - > _cast - > _type ! = kCastText )
return ;
2021-08-18 20:19:01 +08:00
_sprite - > updateEditable ( ) ;
2021-06-27 16:13:00 +08:00
setEditable ( _sprite - > _editable ) ;
if ( _widget ) {
Graphics : : MacText * textWidget = ( Graphics : : MacText * ) _widget ;
// if we got auto expand text, then we update dims to sprite
if ( ! textWidget - > getFixDims ( ) & & ( _sprite - > _width ! = _widget - > _dims . width ( ) | | _sprite - > _height ! = _widget - > _dims . height ( ) ) ) {
_sprite - > _width = _widget - > _dims . width ( ) ;
_sprite - > _height = _widget - > _dims . height ( ) ;
_width = _sprite - > _width ;
_height = _sprite - > _height ;
g_director - > getCurrentWindow ( ) - > addDirtyRect ( _widget - > _dims ) ;
}
}
}
2020-08-13 23:37:10 -04:00
void Channel : : setEditable ( bool editable ) {
if ( _sprite - > _cast & & _sprite - > _cast - > _type = = kCastText ) {
2021-06-04 14:52:19 +08:00
// if the sprite is editable, then we refresh the selEnd and setStart
2020-08-13 23:37:10 -04:00
if ( _widget ) {
2021-05-23 15:25:32 +08:00
( ( Graphics : : MacText * ) _widget ) - > setEditable ( editable ) ;
2021-06-04 15:32:47 +08:00
// we only set the first editable text member in score active
2021-06-04 16:03:45 +08:00
if ( editable ) {
Graphics : : MacWidget * activewidget = g_director - > _wm - > getActiveWidget ( ) ;
if ( activewidget = = nullptr | | ! activewidget - > isEditable ( ) )
g_director - > _wm - > setActiveWidget ( _widget ) ;
}
2020-08-13 23:37:10 -04:00
}
}
}
2021-06-13 22:05:11 +08:00
// we may optimize this by only update those attributes when we are changing them
// but not to pass them to widgets everytime
void Channel : : updateGlobalAttr ( ) {
if ( ! _sprite - > _cast )
return ;
2021-07-17 14:49:43 +08:00
2021-06-13 22:05:11 +08:00
// update text info, including selEnd and selStart
if ( _sprite - > _cast - > _type = = kCastText & & _sprite - > _editable & & _widget )
( ( Graphics : : MacText * ) _widget ) - > setSelRange ( g_director - > getCurrentMovie ( ) - > _selStart , g_director - > getCurrentMovie ( ) - > _selEnd ) ;
2021-07-17 14:49:43 +08:00
2021-06-13 22:05:11 +08:00
// update button info, including checkBoxType
2021-07-28 17:41:52 +08:00
if ( ( _sprite - > _cast - > _type = = kCastButton | | isButtonSprite ( _sprite - > _spriteType ) ) & & _widget ) {
2021-06-13 22:05:11 +08:00
( ( Graphics : : MacButton * ) _widget ) - > setCheckBoxType ( g_director - > getCurrentMovie ( ) - > _checkBoxType ) ;
2021-07-28 17:41:52 +08:00
( ( Graphics : : MacButton * ) _widget ) - > setCheckBoxAccess ( g_director - > getCurrentMovie ( ) - > _checkBoxAccess ) ;
}
2021-06-13 22:05:11 +08:00
}
2020-07-29 12:39:37 -04:00
void Channel : : replaceSprite ( Sprite * nextSprite ) {
if ( ! nextSprite )
return ;
bool newSprite = ( _sprite - > _spriteType = = kInactiveSprite & & nextSprite - > _spriteType ! = kInactiveSprite ) ;
2021-06-29 15:13:30 +08:00
bool widgetKeeped = _sprite - > _cast & & _widget ;
2021-06-21 18:28:29 +08:00
2021-06-21 21:17:44 +08:00
// update the _sprite we stored in channel, and point the originalSprite to the new one
2021-06-22 21:52:14 +08:00
// release the widget, because we may having the new one
2021-06-24 10:29:46 +08:00
if ( _sprite - > _cast & & ! canKeepWidget ( _sprite , nextSprite ) ) {
widgetKeeped = false ;
2021-06-21 19:34:54 +08:00
_sprite - > _cast - > releaseWidget ( ) ;
2021-07-03 22:18:49 +08:00
newSprite = true ;
2021-06-24 10:29:46 +08:00
}
int width = _width ;
int height = _height ;
2021-06-22 21:52:14 +08:00
* _sprite = * nextSprite ;
2020-07-29 12:39:37 -04:00
2021-06-29 11:12:17 +08:00
// TODO: auto expand text size is meaning less for us, not all text
2021-06-24 10:29:46 +08:00
// since we are using initialRect for the text cast member now, then the sprite size is meaning less for us.
// thus, we keep the _sprite size here
if ( hasTextCastMember ( _sprite ) & & widgetKeeped ) {
_sprite - > _width = width ;
_sprite - > _height = height ;
}
2020-07-29 12:39:37 -04:00
// Sprites marked moveable are constrained to the same bounding box until
// the moveable is disabled
2020-07-31 10:37:05 -04:00
if ( ! _sprite - > _moveable | | newSprite )
2020-07-29 12:39:37 -04:00
_currentPoint = _sprite - > _startPoint ;
2020-07-31 10:37:05 -04:00
if ( ! _sprite - > _stretch ) {
_width = _sprite - > _width ;
_height = _sprite - > _height ;
2020-07-29 12:39:37 -04:00
}
}
2020-07-09 22:45:58 -04:00
void Channel : : setWidth ( int w ) {
if ( _sprite - > _puppet & & _sprite - > _stretch ) {
2021-06-26 19:57:42 +08:00
_width = MAX < int > ( w , 0 ) ;
2020-07-09 22:45:58 -04:00
}
}
void Channel : : setHeight ( int h ) {
if ( _sprite - > _puppet & & _sprite - > _stretch ) {
2021-06-26 19:57:42 +08:00
_height = MAX < int > ( h , 0 ) ;
2020-07-09 22:45:58 -04:00
}
}
2020-07-09 23:27:05 -04:00
void Channel : : setBbox ( int l , int t , int r , int b ) {
if ( _sprite - > _puppet & & _sprite - > _stretch ) {
_width = r - l ;
_height = b - t ;
_currentPoint . x = l ;
_currentPoint . y = t ;
addRegistrationOffset ( _currentPoint , true ) ;
}
}
2021-06-22 22:58:06 +08:00
// here is the place for deciding whether the widget can be keep or not
2021-06-24 09:00:22 +08:00
// here's the definition, we first need to have widgets to keep, and the cast is not modified(modified means we need to re-create the widget)
// and the castId should be same while castId should not be zero
2021-06-30 18:41:00 -04:00
bool Channel : : canKeepWidget ( CastMemberID castId ) {
if ( _widget & & _sprite & & _sprite - > _cast & & ! _sprite - > _cast - > isModified ( ) & & castId . member & & castId = = _sprite - > _castId ) {
2021-06-22 22:58:06 +08:00
return true ;
}
return false ;
}
2021-06-24 09:00:22 +08:00
bool Channel : : canKeepWidget ( Sprite * currentSprite , Sprite * nextSprite ) {
2021-06-30 18:41:00 -04:00
if ( _widget & & currentSprite & & currentSprite - > _cast & & nextSprite & & nextSprite - > _cast & & ! currentSprite - > _cast - > isModified ( ) & & currentSprite - > _castId = = nextSprite - > _castId & & currentSprite - > _castId . member ) {
2021-06-24 09:00:22 +08:00
return true ;
}
return false ;
}
2021-06-13 20:09:25 +08:00
// currently, when we are setting hilite, we delete the widget and the re-create it
// so we may optimize this if this operation takes much time
2021-07-03 11:03:12 +08:00
void Channel : : replaceWidget ( CastMemberID previousCastId , bool force ) {
2021-06-22 21:52:14 +08:00
// if the castmember is the same, and we are not modifying anything which cannot be handle by channel. Then we don't replace the widget
2021-07-03 11:03:12 +08:00
if ( ! force & & canKeepWidget ( previousCastId ) ) {
2021-06-30 18:41:00 -04:00
debug ( 5 , " Channel::replaceWidget(): skip deleting %s " , _sprite - > _castId . asString ( ) . c_str ( ) ) ;
2021-06-22 21:52:14 +08:00
return ;
}
2020-07-26 04:32:29 +00:00
if ( _widget ) {
delete _widget ;
_widget = nullptr ;
}
if ( _sprite & & _sprite - > _cast ) {
2021-07-17 14:24:42 +08:00
// use sprite type to guide us how to draw the cast
// if the type don't match, then we will set it as transparent. i.e. don't create widget
if ( ! _sprite - > checkSpriteType ( ) )
return ;
2021-08-09 16:13:31 +08:00
// always use the unstretched dims.
// because only the stretched sprite will have different channel size and sprite size
// we need the original image to scale the sprite.
// for the scaled bitmap castmember, it has scaled dims on sprite size, so we don't have to worry about it.
Common : : Rect bbox ( getBbox ( true ) ) ;
2021-06-29 15:15:39 -04:00
_sprite - > _cast - > setModified ( false ) ;
2020-07-26 04:32:29 +00:00
2021-07-17 14:30:00 +08:00
_widget = _sprite - > _cast - > createWidget ( bbox , this , _sprite - > _spriteType ) ;
2020-07-26 04:32:29 +00:00
if ( _widget ) {
_widget - > _priority = _priority ;
_widget - > draw ( ) ;
2020-08-21 16:17:56 -04:00
if ( _sprite - > _cast - > _type = = kCastText | | _sprite - > _cast - > _type = = kCastButton ) {
2021-07-08 21:28:30 +08:00
2020-08-21 16:17:56 -04:00
_sprite - > _width = _widget - > _dims . width ( ) ;
_sprite - > _height = _widget - > _dims . height ( ) ;
_width = _sprite - > _width ;
_height = _sprite - > _height ;
}
2020-07-26 04:32:29 +00:00
}
}
}
bool Channel : : updateWidget ( ) {
2020-08-07 10:07:32 +02:00
if ( _widget & & _widget - > needsRedraw ( ) ) {
2020-07-26 04:32:29 +00:00
if ( _sprite - > _cast ) {
_sprite - > _cast - > updateFromWidget ( _widget ) ;
}
_widget - > draw ( ) ;
return true ;
}
return false ;
}
2021-08-12 17:02:39 +08:00
bool Channel : : isTrail ( ) {
return _sprite - > _trails ;
}
2020-07-09 23:27:05 -04:00
void Channel : : addRegistrationOffset ( Common : : Point & pos , bool subtract ) {
2020-08-14 00:42:00 +02:00
if ( ! _sprite - > _cast )
return ;
2020-07-08 13:27:41 -04:00
2020-08-14 00:42:00 +02:00
switch ( _sprite - > _cast - > _type ) {
2021-11-09 21:46:44 +08:00
case kCastBitmap :
{
if ( subtract )
pos - = _sprite - > getRegistrationOffset ( ) ;
else
pos + = _sprite - > getRegistrationOffset ( ) ;
2020-07-09 23:27:05 -04:00
}
2021-11-09 21:46:44 +08:00
break ;
2020-08-14 00:42:00 +02:00
case kCastDigitalVideo :
2021-11-06 16:35:12 +08:00
case kCastFilmLoop :
2021-11-09 21:46:44 +08:00
pos - = _sprite - > getRegistrationOffset ( ) ;
2020-08-14 00:42:00 +02:00
default :
break ;
2020-07-08 13:27:41 -04:00
}
2021-11-09 21:46:44 +08:00
return ;
2020-07-08 13:27:41 -04:00
}
void Channel : : addDelta ( Common : : Point pos ) {
// TODO: Channel should have a pointer to its score
if ( _sprite - > _moveable & &
_constraint > 0 & &
_constraint < g_director - > getCurrentMovie ( ) - > getScore ( ) - > _channels . size ( ) ) {
Common : : Rect constraintBbox = g_director - > getCurrentMovie ( ) - > getScore ( ) - > _channels [ _constraint ] - > getBbox ( ) ;
Common : : Rect currentBbox = getBbox ( ) ;
currentBbox . translate ( _delta . x + pos . x , _delta . y + pos . y ) ;
Common : : Point regPoint ;
addRegistrationOffset ( regPoint ) ;
constraintBbox . top + = regPoint . y ;
constraintBbox . bottom - = regPoint . y ;
constraintBbox . left + = regPoint . x ;
constraintBbox . right - = regPoint . x ;
2021-06-27 14:58:55 +08:00
// offset for the boundary
constraintBbox . right + + ;
constraintBbox . bottom + + ;
2020-07-08 13:27:41 -04:00
if ( ! constraintBbox . contains ( currentBbox ) ) {
if ( currentBbox . top < constraintBbox . top ) {
pos . y + = constraintBbox . top - currentBbox . top ;
} else if ( currentBbox . bottom > constraintBbox . bottom ) {
pos . y + = constraintBbox . bottom - currentBbox . bottom ;
}
if ( currentBbox . left < constraintBbox . left ) {
pos . x + = constraintBbox . left - currentBbox . left ;
} else if ( currentBbox . right > constraintBbox . right ) {
pos . x + = constraintBbox . right - currentBbox . right ;
}
}
}
_delta + = pos ;
}
2021-07-30 16:26:36 +08:00
int Channel : : getMouseChar ( int x , int y ) {
if ( _sprite - > _spriteType ! = kTextSprite )
return - 1 ;
if ( ! _widget ) {
warning ( " Channel::getMouseChar getting mouse char on a non-existing widget " ) ;
return - 1 ;
}
return ( ( Graphics : : MacText * ) _widget ) - > getMouseChar ( x , y ) ;
}
2021-07-30 16:38:31 +08:00
int Channel : : getMouseWord ( int x , int y ) {
if ( _sprite - > _spriteType ! = kTextSprite )
return - 1 ;
if ( ! _widget ) {
2021-07-31 15:47:07 +08:00
warning ( " Channel::getMouseWord getting mouse word on a non-existing widget " ) ;
2021-07-30 16:38:31 +08:00
return - 1 ;
}
return ( ( Graphics : : MacText * ) _widget ) - > getMouseWord ( x , y ) ;
}
2021-07-31 15:47:07 +08:00
int Channel : : getMouseItem ( int x , int y ) {
if ( _sprite - > _spriteType ! = kTextSprite )
return - 1 ;
if ( ! _widget ) {
warning ( " Channel::getMouseItem getting mouse item on a non-existing widget " ) ;
return - 1 ;
}
return ( ( Graphics : : MacText * ) _widget ) - > getMouseItem ( x , y ) ;
}
2021-07-31 15:52:57 +08:00
int Channel : : getMouseLine ( int x , int y ) {
if ( _sprite - > _spriteType ! = kTextSprite )
return - 1 ;
if ( ! _widget ) {
warning ( " Channel::getMouseLine getting mouse line on a non-existing widget " ) ;
return - 1 ;
}
return ( ( Graphics : : MacText * ) _widget ) - > getMouseLine ( x , y ) ;
}
2020-07-08 13:27:41 -04:00
Common : : Point Channel : : getPosition ( ) {
Common : : Point res = _currentPoint ;
addRegistrationOffset ( res ) ;
2020-07-24 10:25:27 -04:00
res . x + = ( _sprite - > _width - _width ) / 2 ;
res . y + = ( _sprite - > _height - _height ) / 2 ;
2020-07-08 13:27:41 -04:00
return res ;
}
2021-11-06 16:35:12 +08:00
bool Channel : : hasSubChannels ( ) {
if ( ( _sprite - > _cast ) & & ( _sprite - > _cast - > _type = = kCastFilmLoop ) ) {
return true ;
}
return false ;
}
Common : : Array < Channel > * Channel : : getSubChannels ( ) {
if ( ( ! _sprite - > _cast ) | | ( _sprite - > _cast - > _type ! = kCastFilmLoop ) ) {
warning ( " Channel doesn't have any sub-channels " ) ;
return nullptr ;
}
Common : : Rect bbox = getBbox ( ) ;
return ( ( FilmLoopCastMember * ) _sprite - > _cast ) - > getSubChannels ( bbox , this ) ;
}
2020-07-08 13:27:41 -04:00
} // End of namespace Director