/*==LICENSE==*
CyanWorlds . com Engine - MMOG client , server and tools
Copyright ( C ) 2011 Cyan Worlds , Inc .
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 .
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
along with this program . If not , see < http : //www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program , or any covered work , by linking or
combining it with any of RAD Game Tools Bink SDK , Autodesk 3 ds Max SDK ,
NVIDIA PhysX SDK , Microsoft DirectX SDK , OpenSSL library , Independent
JPEG Group JPEG library , Microsoft Windows Media SDK , or Apple QuickTime SDK
( or a modified version of those libraries ) ,
containing parts covered by the terms of the Bink SDK EULA , 3 ds Max EULA ,
PhysX SDK EULA , DirectX SDK EULA , OpenSSL and SSLeay licenses , IJG
JPEG Library README , Windows Media SDK EULA , or QuickTime SDK EULA , the
licensors of this Program grant you additional
permission to convey the resulting work . Corresponding Source for a
non - source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work .
You can contact Cyan Worlds , Inc . by email legal @ cyan . com
or by snail mail at :
Cyan Worlds , Inc .
14617 N Newport Hwy
Mead , WA 99021
* = = LICENSE = = */
//////////////////////////////////////////////////////////////////////////////
// //
// pfJournalBook Class //
// A generic, high-level, abstract method of creating various Myst-like //
// books within the game with very little effort, while ensuring that they //
// all remain consistent in appearance and operability. //
// //
//////////////////////////////////////////////////////////////////////////////
# include "pfJournalBook.h"
# include <wchar.h>
# include "hsResMgr.h"
# include "pcSmallRect.h"
# include "plgDispatch.h"
# include "pfGameGUIMgr/pfGUIDialogMod.h"
# include "pfGameGUIMgr/pfGUIControlMod.h"
# include "pfGameGUIMgr/pfGUICheckBoxCtrl.h"
# include "pfGameGUIMgr/pfGUIDialogHandlers.h"
# include "pfGameGUIMgr/pfGUIDynDisplayCtrl.h"
# include "pfGameGUIMgr/pfGUIClickMapCtrl.h"
# include "pfGameGUIMgr/pfGUIButtonMod.h"
# include "pfGameGUIMgr/pfGUIProgressCtrl.h"
# include "pfGameGUIMgr/pfGUIMultiLineEditCtrl.h"
# include "pfMessage/pfGUINotifyMsg.h"
# include "plGImage/plMipmap.h"
# include "plGImage/plDynamicTextMap.h"
# include "hsGDeviceRef.h"
# include "plMessage/plAnimCmdMsg.h"
# include "pnKeyedObject/plFixedKey.h"
# include "pnMessage/plRefMsg.h"
# include "pnMessage/plTimeMsg.h"
# include "plMessage/plLayRefMsg.h"
# include "plMessage/plMatRefMsg.h"
# include "plSurface/plLayerInterface.h"
# include "plSurface/plLayer.h"
# include "plSurface/hsGMaterial.h"
# include "plAgeLoader/plAgeLoader.h"
# include "pfSurface/plLayerAVI.h"
// So we can do image searches in our local age
# include "plNetClient/plNetClientMgr.h"
# include "plResMgr/plKeyFinder.h"
// For notify sends
# include "pnMessage/plNotifyMsg.h"
# include "pnTimer/plTimerCallbackManager.h"
# include "plMessage/plTimerCallbackMsg.h"
// For custom cursors
# include "plInputCore/plInputInterface.h"
// For measuring text
# include "plGImage/plFont.h"
// For SFX
# include "hsTimer.h"
//////////////////////////////////////////////////////////////////////////////
//// pfEsHTMLChunk Class /////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
class pfEsHTMLChunk
{
public :
std : : wstring fText ; // Paragraph text, or face name
plKey fImageKey ; // Key of image
uint8_t fFontSize ;
uint32_t fFlags ;
uint8_t fType ;
uint32_t fEventID ;
pcSmallRect fLinkRect ; // Used only for image chunks, and only when stored in the fVisibleLinks array
hsColorRGBA fColor ;
uint16_t fAbsoluteX , fAbsoluteY ;
float fCurrOpacity ; // For SFX images
float fSFXTime ; // For SFX images
float fMinOpacity , fMaxOpacity ;
hsColorRGBA fCurrColor ;
hsColorRGBA fOffColor , fOnColor ;
bool fNoResizeImg ;
int16_t fLineSpacing ;
bool fTintDecal ;
bool fLoopMovie ;
bool fOnCover ; // if true, the movie is on the cover
uint8_t fMovieIndex ; // the index of the movie in the source code, used for identification
enum Flags
{
kFontBold = 0x00000001 ,
kFontItalic = 0x00000002 ,
kFontRegular = 0x00000004 , // 'cause 0 means "style not defined"
kFontMask = kFontBold | kFontItalic | kFontRegular ,
kFontColor = 0x00000008 ,
kFontSpacing = 0x00000010 ,
kCenter = 0x00000001 ,
kLeft = 0x00000002 ,
kRight = 0x00000003 ,
kAlignMask = 0x00000003 ,
kBlendAlpha = 0x00000004 ,
kCanLink = 0x00000008 ,
kFloating = 0x00000010 ,
kGlowing = 0x00000020 ,
kActAsCB = 0x00000040 , // Cause the image to act in a checkbox-like fashion.
// Min opacity turns into "off opacity" and max opacity
// is "on opacity"
kChecked = 0x00000080 , // Only for kActAsCB, set if it's currently "checked"
kTranslucent = 0x00000100 // is the image translucent? if so, use fCurrOpacity
} ;
enum Types
{
kEmpty = 0 ,
kParagraph ,
kImage ,
kPageBreak ,
kFontChange ,
kMargin ,
kCover , // Just a placeholder, never actually used after compile time
kBook , // another placeholder
kDecal ,
kMovie ,
kEditable // placeholder, ver 3.0
} ;
// Paragraph constructor
pfEsHTMLChunk ( const wchar_t * text )
{
fType = kParagraph ;
if ( text )
fText = text ;
else
fText = L " " ;
fFlags = kLeft ;
fFontSize = 0 ;
fImageKey = nil ;
fEventID = 0 ;
fColor . Set ( 0.f , 0.f , 0.f , 1.f ) ;
fAbsoluteX = fAbsoluteY = 0 ;
fCurrOpacity = 1.f ;
fMinOpacity = 0.f ;
fMaxOpacity = 1.f ;
fNoResizeImg = false ;
fLineSpacing = 0 ;
fTintDecal = false ;
fLoopMovie = true ;
fOnCover = false ;
fMovieIndex = - 1 ;
}
// Image constructor (used for decals and movies too)
pfEsHTMLChunk ( plKey imageKey , uint32_t alignFlags )
{
fType = kImage ;
fText = L " " ;
fFlags = alignFlags ;
fFontSize = 0 ;
fImageKey = imageKey ;
fEventID = 0 ;
fColor . Set ( 0.f , 0.f , 0.f , 1.f ) ;
fAbsoluteX = fAbsoluteY = 0 ;
fCurrOpacity = 1.f ;
fMinOpacity = 0.f ;
fMaxOpacity = 1.f ;
fNoResizeImg = false ;
fLineSpacing = 0 ;
fTintDecal = false ;
fLoopMovie = true ;
fOnCover = false ;
fMovieIndex = - 1 ;
}
// Page break constructor
pfEsHTMLChunk ( )
{
fType = kPageBreak ;
fText = L " " ;
fImageKey = nil ;
fFontSize = 0 ;
fFlags = 0 ;
fEventID = 0 ;
fColor . Set ( 0.f , 0.f , 0.f , 1.f ) ;
fAbsoluteX = fAbsoluteY = 0 ;
fCurrOpacity = 1.f ;
fMinOpacity = 0.f ;
fMaxOpacity = 1.f ;
fNoResizeImg = false ;
fLineSpacing = 0 ;
fTintDecal = false ;
fLoopMovie = true ;
fOnCover = false ;
fMovieIndex = - 1 ;
}
// Font change constructor
pfEsHTMLChunk ( const wchar_t * face , uint8_t size , uint32_t fontFlags )
{
fType = kFontChange ;
if ( face )
fText = face ;
else
fText = L " " ;
fFontSize = size ;
fFlags = fontFlags ;
fImageKey = nil ;
fEventID = 0 ;
fColor . Set ( 0.f , 0.f , 0.f , 1.f ) ;
fAbsoluteX = fAbsoluteY = 0 ;
fCurrOpacity = 1.f ;
fMinOpacity = 0.f ;
fMaxOpacity = 1.f ;
fNoResizeImg = false ;
fLineSpacing = 0 ;
fTintDecal = false ;
fLoopMovie = true ;
fOnCover = false ;
fMovieIndex = - 1 ;
}
~ pfEsHTMLChunk ( ) { }
} ;
//////////////////////////////////////////////////////////////////////////////
//// Our Template Dialog Handler /////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
class pfJournalDlgProc : public pfGUIDialogProc
{
protected :
pfBookData * fBook ;
public :
enum TagIDs
{
kTagBookCover = 100 ,
kTagLeftDTMap = 101 ,
kTagRightDTMap = 102 ,
kTagTurnFrontDTMap = 103 ,
kTagTurnBackDTMap = 104 ,
kTagTurnPageCtrl = 105 ,
kTagLeftPageBtn = 106 ,
kTagRightPageBtn = 107 ,
kTagOutsideBookBtn = 108 ,
kTagCoverLayer = 109 ,
kTagLeftCornerBtn = 110 ,
kTagRightCornerBtn = 111 ,
kTagWidthCBDummy = 112 ,
kTagHeightCBDummy = 113 ,
kTagLeftEditCtrl = 120 ,
kTagRightEditCtrl = 121 ,
kTagTurnFrontEditCtrl = 122 ,
kTagTurnBackEditCtrl = 123
} ;
pfJournalDlgProc ( pfBookData * book ) : fBook ( book )
{
}
virtual ~ pfJournalDlgProc ( )
{
}
virtual void DoSomething ( pfGUIControlMod * ctrl )
{
if ( fBook & & fBook - > fCurrBook )
{
if ( ctrl - > GetTagID ( ) = = kTagBookCover )
{
fBook - > fCurrBook - > Open ( ) ;
}
else if ( ctrl - > GetTagID ( ) = = kTagLeftPageBtn )
{
// only turn pages if the book is actually open
if ( fBook - > fCurrentlyOpen )
fBook - > fCurrBook - > IHandleLeftSideClick ( ) ;
}
else if ( ctrl - > GetTagID ( ) = = kTagRightPageBtn )
{
// only turn pages if the book is actually open
if ( fBook - > fCurrentlyOpen )
fBook - > fCurrBook - > IHandleRightSideClick ( ) ;
}
else if ( ctrl - > GetTagID ( ) = = kTagOutsideBookBtn )
{
if ( fBook - > fCurrentlyOpen )
fBook - > fCurrBook - > CloseAndHide ( ) ;
else
fBook - > fCurrBook - > Hide ( ) ;
}
}
}
// Called on dialog init (i.e. first showing, before OnShow() is called), only ever called once
virtual void OnInit ( void )
{
}
// Called before the dialog is shown, always after OnInit()
virtual void OnShow ( void )
{
}
// Called before the dialog is hidden
virtual void OnHide ( void )
{
}
// Called on the dialog's destructor, before it's unregistered with the game GUI manager
virtual void OnDestroy ( void )
{
}
// Called when the dialog's focused control changes
virtual void OnCtrlFocusChange ( pfGUIControlMod * oldCtrl , pfGUIControlMod * newCtrl )
{
}
// Called when the key bound to a GUI event is pressed. Only called on the top modal dialog
virtual void OnControlEvent ( ControlEvt event )
{
if ( event = = kExitMode )
{
if ( fBook - > fCurrentlyOpen )
fBook - > fCurrBook - > CloseAndHide ( ) ;
else
fBook - > fCurrBook - > Hide ( ) ;
}
}
virtual void HandleExtendedEvent ( pfGUIControlMod * ctrl , uint32_t event )
{
if ( fBook )
{
if ( ctrl = = fBook - > fLeftPageMap )
{
if ( event = = pfGUIClickMapCtrl : : kMouseHovered )
{
if ( fBook - > fCurrBook )
{
// Update our custom cursor on the map
int32_t idx = fBook - > fCurrBook - > IFindCurrVisibleLink ( false , true ) ;
if ( idx ! = - 1 )
fBook - > fLeftPageMap - > SetCustomCursor ( plInputInterface : : kCursorPoised /*Hand*/ ) ;
else if ( ( fBook - > fCurrBook - > fCurrentPage > 1 ) & & ( fBook - > fCurrBook - > fAllowTurning ) )
fBook - > fLeftPageMap - > SetCustomCursor ( plInputInterface : : kCursorLeft ) ;
else if ( ( fBook - > fCurrBook - > fAreEditing ) & & ! ( fBook - > fLeftEditCtrl - > ShowingBeginningOfBuffer ( ) ) ) // if we have more buffer to show
fBook - > fLeftPageMap - > SetCustomCursor ( plInputInterface : : kCursorLeft ) ;
else
fBook - > fLeftPageMap - > SetCustomCursor ( plInputInterface : : kCursorUp ) ;
}
}
}
else if ( ctrl = = fBook - > fRightPageMap )
{
if ( event = = pfGUIClickMapCtrl : : kMouseHovered )
{
if ( fBook - > fCurrBook )
{
// Update our custom cursor on the map
int32_t idx = fBook - > fCurrBook - > IFindCurrVisibleLink ( true , true ) ;
if ( idx ! = - 1 )
fBook - > fRightPageMap - > SetCustomCursor ( plInputInterface : : kCursorPoised /*Hand*/ ) ;
else if ( ( fBook - > fCurrBook - > fAreWeShowing ) & & ( fBook - > fCurrBook - > fCurrentPage + 2 < = fBook - > fCurrBook - > fLastPage ) & & ( fBook - > fCurrBook - > fAllowTurning ) )
fBook - > fRightPageMap - > SetCustomCursor ( plInputInterface : : kCursorRight ) ;
else if ( ( fBook - > fCurrBook - > fAreEditing ) & & ! ( fBook - > fRightEditCtrl - > ShowingEndOfBuffer ( ) ) ) // if we have more buffer to show
fBook - > fRightPageMap - > SetCustomCursor ( plInputInterface : : kCursorRight ) ;
else
fBook - > fRightPageMap - > SetCustomCursor ( plInputInterface : : kCursorUp ) ;
}
}
}
}
}
} ;
//// Multiline edit handler class ////////////////////////////////////////////
class pfBookMultiLineEditProc : public pfGUIMultiLineEditProc
{
private :
pfBookData * bookData ;
public :
pfBookMultiLineEditProc ( pfBookData * owner ) { bookData = owner ; }
virtual ~ pfBookMultiLineEditProc ( ) { }
virtual void OnEndOfControlList ( int32_t cursorPos ) { bookData - > HitEndOfControlList ( cursorPos ) ; }
virtual void OnBeginningOfControlList ( int32_t cursorPos ) { bookData - > HitBeginningOfControlList ( cursorPos ) ; }
} ;
//// Book data class /////////////////////////////////////////////////////////
pfBookData : : pfBookData ( const plString & guiName /* = nil */ )
{
fCurrBook = nil ;
fDialog = nil ;
fCoverButton = fTurnPageButton = nil ;
fLeftPageMap = fRightPageMap = nil ;
fCoverLayer = nil ;
fCoverMaterial = nil ;
uint16_t i ;
for ( i = 0 ; i < 4 ; i + + )
fPageMaterials [ i ] = nil ;
fLeftCorner = fRightCorner = nil ;
fWidthCtrl = fHeightCtrl = nil ;
fCurrSFXPages = kNoSides ;
fBaseSFXTime = 0.f ;
fResetSFXFlag = false ;
fSFXUpdateFlip = false ;
fCurrentlyTurning = false ;
fRightEditCtrl = fLeftEditCtrl = nil ;
fTurnFrontEditCtrl = fTurnBackEditCtrl = nil ;
fEditable = false ;
fAdjustCursorTo = - 1 ;
if ( ! guiName . IsEmpty ( ) )
fGUIName = guiName ;
else
fGUIName = " BkBook " ;
}
pfBookData : : ~ pfBookData ( )
{
RegisterForSFX ( kNoSides ) ;
}
void pfBookData : : LoadGUI ( )
{
// has the dialog been loaded yet?
if ( ! pfGameGUIMgr : : GetInstance ( ) - > IsDialogLoaded ( fGUIName . c_str ( ) ) )
// no then load and set handler
pfGameGUIMgr : : GetInstance ( ) - > LoadDialog ( fGUIName . c_str ( ) , GetKey ( ) , " GUI " ) ;
else
// yes then just set the handler
pfGameGUIMgr : : GetInstance ( ) - > SetDialogToNotify ( fGUIName . c_str ( ) , GetKey ( ) ) ;
}
bool pfBookData : : MsgReceive ( plMessage * pMsg )
{
plGenRefMsg * ref = plGenRefMsg : : ConvertNoRef ( pMsg ) ;
if ( ref ! = nil )
{
if ( ref - > fType = = kRefDialog )
{
if ( ref - > GetContext ( ) & ( plRefMsg : : kOnCreate | plRefMsg : : kOnRequest | plRefMsg : : kOnReplace ) )
{
pfGUIDialogMod * temp = pfGUIDialogMod : : ConvertNoRef ( ref - > GetRef ( ) ) ;
if ( temp ! = nil ) // sanity check
fDialog = temp ;
}
/*else
{
fDialog = nil ;
fCoverButton = nil ;
} */
return true ;
}
else if ( ref - > fType = = kRefDefaultCover )
{
if ( ref - > GetContext ( ) & ( plRefMsg : : kOnCreate | plRefMsg : : kOnRequest | plRefMsg : : kOnReplace ) )
fDefaultCover = plMipmap : : ConvertNoRef ( ref - > GetRef ( ) ) ;
else
fDefaultCover = nil ;
return true ;
}
}
plEventCallbackMsg * callback = plEventCallbackMsg : : ConvertNoRef ( pMsg ) ;
if ( callback ! = nil )
{
// Our callback message to tell us the page is done flipping
if ( callback - > fUser & 0x08 )
{
// make sure that we still have a current book
if ( fCurrBook )
{
// Or actually maybe it's that we're done closing and should hide now,
// produced from a CloseAndHide()
if ( callback - > fEvent = = kStop )
fCurrBook - > Hide ( ) ;
else
fCurrBook - > IFinishShow ( ( callback - > fUser & 0x01 ) ? true : false ) ;
}
}
else if ( fCurrentlyTurning )
{
if ( callback - > fUser & 0x04 )
IFillUncoveringPage ( ( bool ) ( callback - > fUser & 0x01 ) ? true : false ) ;
else if ( callback - > fUser & 0x02 )
StartTriggeredFlip ( ( bool ) ( callback - > fUser & 0x01 ) ? true : false ) ;
else
IFinishTriggeredFlip ( ( bool ) ( callback - > fUser & 0x01 ) ? true : false ) ;
}
return true ;
}
pfGUINotifyMsg * notify = pfGUINotifyMsg : : ConvertNoRef ( pMsg ) ;
if ( notify ! = nil )
{
// The only time we should get this is when the dialog loads; after that, we hijack
// the dialog proc with our own
IInitTemplate ( pfGUIDialogMod : : ConvertNoRef ( notify - > GetSender ( ) - > ObjectIsLoaded ( ) ) ) ;
return true ;
}
plTimeMsg * time = plTimeMsg : : ConvertNoRef ( pMsg ) ;
if ( time ! = nil & & fCurrSFXPages ! = kNoSides & & ! fCurrentlyTurning & & fCurrentlyOpen )
{
IHandleSFX ( ( float ) time - > DSeconds ( ) ) ;
return true ;
}
plTimerCallbackMsg * timerMsg = plTimerCallbackMsg : : ConvertNoRef ( pMsg ) ;
if ( timerMsg )
{
if ( timerMsg - > fID = = 99 ) // the flip animation is about to end, hide the page to prevent flickering
{
// the right side was uncovered, so the left side needs to be hidden
fLeftEditCtrl - > SetVisible ( false ) ;
}
else if ( timerMsg - > fID = = 98 )
{
// the left side was uncovered, so the right side needs to be hidden
fRightEditCtrl - > SetVisible ( false ) ;
}
}
return hsKeyedObject : : MsgReceive ( pMsg ) ;
}
void pfBookData : : IInitTemplate ( pfGUIDialogMod * templateDlg )
{
hsAssert ( templateDlg ! = nil , " Nil template in pfBookData::IInitTemplate()! " ) ;
// Init and ref our fDialog pointer
hsgResMgr : : ResMgr ( ) - > SendRef ( templateDlg - > GetKey ( ) , new plGenRefMsg ( GetKey ( ) , plRefMsg : : kOnCreate , - 1 , kRefDialog ) , plRefFlags : : kPassiveRef ) ;
// Hijack the dialog proc with our own
templateDlg - > SetHandlerForAll ( new pfJournalDlgProc ( this ) ) ;
// Find our animation keys
// And other interesting pointers
fCoverButton = pfGUICheckBoxCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagBookCover ) ) ;
fTurnPageButton = pfGUICheckBoxCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagTurnPageCtrl ) ) ;
fLeftPageMap = pfGUIClickMapCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagLeftPageBtn ) ) ;
fRightPageMap = pfGUIClickMapCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagRightPageBtn ) ) ;
fLeftCorner = pfGUIButtonMod : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagLeftCornerBtn ) ) ;
fRightCorner = pfGUIButtonMod : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagRightCornerBtn ) ) ;
fWidthCtrl = pfGUIProgressCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagWidthCBDummy ) ) ;
fHeightCtrl = pfGUIProgressCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagHeightCBDummy ) ) ;
fTurnPageButton - > SetEnabled ( false ) ;
fCoverButton - > DontPlaySounds ( ) ; // dont let checkbox play sounds, journal will take care of that.
fCoverLayer = pfGUIDynDisplayCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagCoverLayer ) ) - > GetLayer ( 0 ) ;
fCoverMaterial = pfGUIDynDisplayCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagCoverLayer ) ) - > GetMaterial ( 0 ) ;
fPageMaterials [ kLeftPage ] = pfGUIDynDisplayCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagLeftDTMap ) ) - > GetMaterial ( 0 ) ;
fPageMaterials [ kRightPage ] = pfGUIDynDisplayCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagRightDTMap ) ) - > GetMaterial ( 0 ) ;
fPageMaterials [ kTurnFrontPage ] = pfGUIDynDisplayCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagTurnFrontDTMap ) ) - > GetMaterial ( 0 ) ;
fPageMaterials [ kTurnBackPage ] = pfGUIDynDisplayCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagTurnBackDTMap ) ) - > GetMaterial ( 0 ) ;
// Grab and ref the default cover mipmap
plLayer * lay = plLayer : : ConvertNoRef ( fCoverLayer ) ;
if ( ( lay ! = nil ) & & ( lay - > GetTexture ( ) ! = nil ) )
hsgResMgr : : ResMgr ( ) - > AddViaNotify ( lay - > GetTexture ( ) - > GetKey ( ) , new plGenRefMsg ( GetKey ( ) , plRefMsg : : kOnCreate , - 1 , kRefDefaultCover ) , plRefFlags : : kPassiveRef ) ;
fLeftPageMap - > SetFlag ( pfGUIClickMapCtrl : : kReportHovering ) ;
fRightPageMap - > SetFlag ( pfGUIClickMapCtrl : : kReportHovering ) ;
fLeftEditCtrl = pfGUIMultiLineEditCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagLeftEditCtrl ) ) ;
if ( fLeftEditCtrl )
{
fLeftEditCtrl - > SetEnabled ( false ) ; // disable the edit controls initially, we can turn them on later
fLeftEditCtrl - > SetVisible ( false ) ;
}
fRightEditCtrl = pfGUIMultiLineEditCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagRightEditCtrl ) ) ;
if ( fRightEditCtrl )
{
fRightEditCtrl - > SetEnabled ( false ) ;
fRightEditCtrl - > SetVisible ( false ) ;
}
fTurnFrontEditCtrl = pfGUIMultiLineEditCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) ) ;
if ( fTurnFrontEditCtrl )
{
fTurnFrontEditCtrl - > SetEnabled ( false ) ;
fTurnFrontEditCtrl - > SetVisible ( false ) ;
}
fTurnBackEditCtrl = pfGUIMultiLineEditCtrl : : ConvertNoRef ( templateDlg - > GetControlFromTag ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) ) ;
if ( fTurnBackEditCtrl )
{
fTurnBackEditCtrl - > SetEnabled ( false ) ;
fTurnBackEditCtrl - > SetVisible ( false ) ;
}
// if all the edit controls are here, we are editable, so set up the initial link
if ( fLeftEditCtrl & & fRightEditCtrl & & fTurnFrontEditCtrl & & fTurnBackEditCtrl )
{
fEditable = true ;
fLeftEditCtrl - > SetNext ( fRightEditCtrl ) ;
fLeftEditCtrl - > SetEventProc ( new pfBookMultiLineEditProc ( this ) ) ;
fTurnFrontEditCtrl - > SetNext ( fTurnBackEditCtrl ) ; // these are always back to back
}
}
//// IGetDTMap ///////////////////////////////////////////////////////////////
// Just a quick helper
plDynamicTextMap * pfBookData : : GetDTMap ( uint32_t which )
{
pfGUIDynDisplayCtrl * display = pfGUIDynDisplayCtrl : : ConvertNoRef ( fDialog - > GetControlFromTag ( which ) ) ;
return display - > GetMap ( 0 ) ;
}
//// GetEditCtrl /////////////////////////////////////////////////////////////
pfGUIMultiLineEditCtrl * pfBookData : : GetEditCtrl ( uint32_t which )
{
switch ( which )
{
case pfJournalDlgProc : : kTagLeftEditCtrl :
return fLeftEditCtrl ;
case pfJournalDlgProc : : kTagRightEditCtrl :
return fRightEditCtrl ;
case pfJournalDlgProc : : kTagTurnFrontEditCtrl :
return fTurnFrontEditCtrl ;
case pfJournalDlgProc : : kTagTurnBackEditCtrl :
return fTurnBackEditCtrl ;
default :
return nil ;
}
}
//// IRegisterForSFX /////////////////////////////////////////////////////////
// Registers (or unregisters) for time messages so we can process special FX
// if we need to
void pfBookData : : RegisterForSFX ( WhichSide whichPages )
{
if ( whichPages = = fCurrSFXPages )
return ;
if ( whichPages ! = kNoSides )
{
plgDispatch : : Dispatch ( ) - > RegisterForExactType ( plTimeMsg : : Index ( ) , GetKey ( ) ) ;
}
else
plgDispatch : : Dispatch ( ) - > UnRegisterForExactType ( plTimeMsg : : Index ( ) , GetKey ( ) ) ;
fCurrSFXPages = whichPages ;
}
//// IHandleSFX //////////////////////////////////////////////////////////////
// Process SFX for this frame. Note: if ANYTHING is wrong (page starts not
// calced, pointers bad, etc) just bail, since the SFX are just for visual
// flair and not really needed.
void pfBookData : : IHandleSFX ( float currTime , WhichSide whichSide /*= kNoSides*/ )
{
if ( fCurrBook = = nil )
return ;
if ( whichSide = = kNoSides )
{
if ( fResetSFXFlag )
{
fBaseSFXTime = currTime ;
fResetSFXFlag = false ;
}
fSFXUpdateFlip = ! fSFXUpdateFlip ;
// Slightly recursive here to help us out a bit
if ( fSFXUpdateFlip & & ( fCurrSFXPages & kLeftSide ) )
IHandleSFX ( currTime , kLeftSide ) ;
else if ( ! fSFXUpdateFlip & & ( fCurrSFXPages & kRightSide ) )
IHandleSFX ( currTime , kRightSide ) ;
return ;
}
// Update all SFX images for this page first
float deltaT = currTime - fBaseSFXTime ;
uint32_t idx , inc = ( whichSide = = kLeftSide ) ? 0 : 1 ;
if ( fCurrBook - > fPageStarts . GetCount ( ) < = fCurrBook - > fCurrentPage + inc + 1 )
return ;
bool stillWant = false ;
for ( idx = fCurrBook - > fPageStarts [ fCurrBook - > fCurrentPage + inc ] ; idx < fCurrBook - > fPageStarts [ fCurrBook - > fCurrentPage + inc + 1 ] ; idx + + )
{
pfEsHTMLChunk * chunk = fCurrBook - > fHTMLSource [ idx ] ;
if ( chunk - > fFlags & pfEsHTMLChunk : : kGlowing )
{
// Glow SFX: animate opacity based on time offset
uint8_t isOdd = 0 ;
float newDelta = deltaT ;
while ( newDelta > chunk - > fSFXTime )
{
isOdd = ~ isOdd ;
newDelta - = chunk - > fSFXTime ;
}
// If we're not odd, then we're decreasing in opacity, else we're increasing
if ( isOdd )
newDelta = chunk - > fSFXTime - newDelta ;
chunk - > fCurrOpacity = chunk - > fMaxOpacity - ( ( chunk - > fMaxOpacity - chunk - > fMinOpacity ) * ( newDelta / chunk - > fSFXTime ) ) ;
stillWant = true ;
}
else if ( chunk - > fFlags & pfEsHTMLChunk : : kActAsCB )
{
// If our opacity doesn't match our checked state, slowly fade to it
hsColorRGBA inc ;
inc . Set ( 0.1f , 0.1f , 0.1f , 0.1f ) ;
hsColorRGBA & want = ( chunk - > fFlags & pfEsHTMLChunk : : kChecked ) ? chunk - > fOnColor : chunk - > fOffColor ;
if ( want ! = chunk - > fCurrColor )
{
# define COMPARE_ME( wnt, curr ) \
if ( wnt > curr + 0.1f ) \
{ \
curr + = 0.1f ; stillWant = true ; \
} \
else if ( wnt < curr - 0.1f ) \
{ \
curr - = 0.1f ; stillWant = true ; \
} \
else \
curr = wnt ;
COMPARE_ME ( want . r , chunk - > fCurrColor . r )
COMPARE_ME ( want . g , chunk - > fCurrColor . g )
COMPARE_ME ( want . b , chunk - > fCurrColor . b )
COMPARE_ME ( want . a , chunk - > fCurrColor . a )
}
}
}
// All done updating that page. Now render it!
fCurrBook - > IRenderPage ( fCurrBook - > fCurrentPage + inc , ( whichSide = = kLeftSide ) ? pfJournalDlgProc : : kTagLeftDTMap : pfJournalDlgProc : : kTagRightDTMap ) ;
if ( ! stillWant )
{
// Done with FX for this page, so unregister for FX on this page now
RegisterForSFX ( ( WhichSide ) ( fCurrSFXPages & ~ whichSide ) ) ;
}
}
//// IFillUncoveringPage /////////////////////////////////////////////////////
// Yet another step in the page flip, to make SURE we're already showing the
// turning page before we fill in the page behind it
void pfBookData : : IFillUncoveringPage ( bool rightSide )
{
// only show the turning page if the book is open
if ( CurrentlyOpen ( ) )
fTurnPageButton - > SetVisible ( true ) ;
// make sure there is a current book
if ( fCurrBook )
{
if ( fCurrBook - > fAreEditing )
{
int id ;
UpdatePageCorners ( rightSide ? kRightSide : kLeftSide ) ;
if ( rightSide )
{
fTurnBackEditCtrl - > ForceUpdate ( ) ; // force everything that is changing to update
fTurnBackEditCtrl - > SetVisible ( true ) ; // and make sure everything is showing
fTurnFrontEditCtrl - > ForceUpdate ( ) ;
fTurnFrontEditCtrl - > SetVisible ( true ) ;
fRightEditCtrl - > ForceUpdate ( ) ;
fRightEditCtrl - > SetVisible ( true ) ;
// The left edit ctrl doesn't update until the page flip animation is done
id = 99 ;
}
else
{
fTurnFrontEditCtrl - > ForceUpdate ( ) ;
fTurnFrontEditCtrl - > SetVisible ( true ) ;
fTurnBackEditCtrl - > ForceUpdate ( ) ;
fTurnBackEditCtrl - > SetVisible ( true ) ;
fLeftEditCtrl - > ForceUpdate ( ) ;
fLeftEditCtrl - > SetVisible ( true ) ;
// The right edit ctrl doesn't update until the page flip animation is done
id = 98 ;
}
// create a timer so we can hide the old left or right turn page right before the animation finishes to prevent flicker
plTimerCallbackMsg * pTimerMsg = new plTimerCallbackMsg ( GetKey ( ) , id ) ;
plgTimerCallbackMgr : : NewTimer ( .5 , pTimerMsg ) ; // .5 found by trial and error
return ; // the gui controls render everything for us, so ignoring this request
}
if ( rightSide )
fCurrBook - > IRenderPage ( fCurrBook - > fCurrentPage + 1 , pfJournalDlgProc : : kTagRightDTMap ) ;
else
fCurrBook - > IRenderPage ( fCurrBook - > fCurrentPage , pfJournalDlgProc : : kTagLeftDTMap ) ;
}
// Update the page corner we're flipping away from
UpdatePageCorners ( rightSide ? kRightSide : kLeftSide ) ;
}
//// ITriggerPageFlip ////////////////////////////////////////////////////////
// Triggers the start of the page-flipping animation, as well as sets up the callback for when it's finished
void pfBookData : : ITriggerPageFlip ( bool flipBackwards , bool immediate )
{
// Hack here: since we don't have an official interface to select these directly
// in MAX, we just use a GUI check box to grab them for us, even though we never
// actually use the functionality of the checkbox itself
const hsTArray < plKey > & keys = fTurnPageButton - > GetAnimationKeys ( ) ;
plString animName = fTurnPageButton - > GetAnimationName ( ) ;
plAnimCmdMsg * msg = new plAnimCmdMsg ( ) ;
if ( immediate )
{
msg - > SetCmd ( plAnimCmdMsg : : kGoToEnd ) ;
}
else
{
msg - > SetCmd ( plAnimCmdMsg : : kContinue ) ;
msg - > SetCmd ( plAnimCmdMsg : : kSetForewards ) ;
}
msg - > SetAnimName ( flipBackwards ? " backward " : " forward " ) ;
msg - > AddReceivers ( keys ) ;
// Here's the whole reason why we're not just checking the checkbox: so we can attach a callback
// so we know when the animation completes. Pretty sad, huh? Poor checkbox.
plEventCallbackMsg * eventMsg = new plEventCallbackMsg ;
eventMsg - > AddReceiver ( GetKey ( ) ) ;
eventMsg - > fRepeats = 0 ;
if ( immediate )
{
eventMsg - > fUser = ( ( ! flipBackwards ) ? 0x01 : 0x00 ) | 0x02 ;
eventMsg - > fEvent = kSingleFrameAdjust ;
}
else
{
eventMsg - > fUser = ( flipBackwards ? 0x01 : 0x00 ) ;
eventMsg - > fEvent = kStop ;
}
msg - > SetCmd ( plAnimCmdMsg : : kAddCallbacks ) ;
msg - > AddCallback ( eventMsg ) ;
hsRefCnt_SafeUnRef ( eventMsg ) ;
if ( ! immediate )
{
// We want a second callback to tell us when, indeed, the page has started turning
// and is thus visible and thus we can actually, really, honestly, safely fill in the
// page behind it
eventMsg = new plEventCallbackMsg ;
eventMsg - > AddReceiver ( GetKey ( ) ) ;
eventMsg - > fRepeats = 0 ;
eventMsg - > fUser = ! flipBackwards ? ( 0x04 | 0x01 ) : 0x04 ;
eventMsg - > fEvent = kBegin ; // Should cause it to be triggered once it seeks at the start of the command
msg - > AddCallback ( eventMsg ) ;
hsRefCnt_SafeUnRef ( eventMsg ) ;
}
fCurrentlyTurning = true ;
msg - > Send ( ) ;
}
//// StartTriggeredFlip /////////////////////////////////////////////////////
// Finishes the start of the triggered page flip (once we're sure the
// animation is at the new frame)
void pfBookData : : StartTriggeredFlip ( bool flipBackwards )
{
if ( flipBackwards )
{
ITriggerPageFlip ( true , false ) ;
}
else
{
ITriggerPageFlip ( false , false ) ;
}
}
//// Kill the page flipping cause, we're closing the book
void pfBookData : : KillPageFlip ( )
{
if ( fCurrentlyTurning )
{
//ITriggerPageFlip(false, true);
fTurnPageButton - > SetVisible ( false ) ;
}
}
//// IFinishTriggeredFlip ////////////////////////////////////////////////////
// Finishes the triggered page flip, on callback
void pfBookData : : IFinishTriggeredFlip ( bool wasBackwards )
{
if ( fCurrBook & & fCurrBook - > fAreEditing ) // this is handled differently when we are editing
{
fLeftEditCtrl - > SetNext ( fRightEditCtrl ) ; // relink the original path
if ( ! wasBackwards )
{
// adjust the starting point of the control (not needed if we weren't backwards since that was done when we started turning
int32_t newStart = fRightEditCtrl - > GetLastVisibleLine ( ) ;
fLeftEditCtrl - > SetGlobalStartLine ( newStart ) ;
}
if ( fAdjustCursorTo > = 0 )
{
if ( wasBackwards )
{
fRightEditCtrl - > SetCursorToLoc ( fAdjustCursorTo ) ;
fRightEditCtrl - > GetOwnerDlg ( ) - > SetFocus ( fRightEditCtrl ) ;
}
else
{
fLeftEditCtrl - > SetCursorToLoc ( fAdjustCursorTo ) ;
fLeftEditCtrl - > GetOwnerDlg ( ) - > SetFocus ( fLeftEditCtrl ) ;
}
fAdjustCursorTo = - 1 ;
}
fTurnFrontEditCtrl - > SetVisible ( false ) ; // hide the controls
fTurnBackEditCtrl - > SetVisible ( false ) ;
fLeftEditCtrl - > SetVisible ( true ) ;
fRightEditCtrl - > SetVisible ( true ) ;
fLeftEditCtrl - > ForceUpdate ( ) ;
fRightEditCtrl - > ForceUpdate ( ) ;
}
else if ( wasBackwards )
{
// Grab the DTMaps for the front of the flip page and the right page, so we can
// copy the front into the right page
plDynamicTextMap * turnFront = GetDTMap ( pfJournalDlgProc : : kTagTurnFrontDTMap ) ;
plDynamicTextMap * right = GetDTMap ( pfJournalDlgProc : : kTagRightDTMap ) ;
// right->Swap( turnFront );
if ( turnFront - > IsValid ( ) & & right - > IsValid ( ) )
{
memcpy ( right - > GetImage ( ) , turnFront - > GetImage ( ) , right - > GetLevelSize ( 0 ) ) ;
if ( right - > GetDeviceRef ( ) ! = nil )
right - > GetDeviceRef ( ) - > SetDirty ( true ) ;
}
// we are going to attempt to re-render the left-hand page
// sometimes, the book stutters on a page flip and screws up the book
if ( fCurrBook )
{
fCurrBook - > IRenderPage ( fCurrBook - > fCurrentPage , pfJournalDlgProc : : kTagLeftDTMap ) ;
// move the videos over
fCurrBook - > IMoveMovies ( PageMaterial ( kTurnFrontPage ) , PageMaterial ( kRightPage ) ) ;
}
}
else
{
// Grab the DTMaps for the back of the flip page and the left page, so we can
// copy the back into the left page
plDynamicTextMap * turnBack = GetDTMap ( pfJournalDlgProc : : kTagTurnBackDTMap ) ;
plDynamicTextMap * left = GetDTMap ( pfJournalDlgProc : : kTagLeftDTMap ) ;
// right->Swap( turnFront );
if ( turnBack - > IsValid ( ) & & left - > IsValid ( ) )
{
memcpy ( left - > GetImage ( ) , turnBack - > GetImage ( ) , left - > GetLevelSize ( 0 ) ) ;
if ( left - > GetDeviceRef ( ) ! = nil )
left - > GetDeviceRef ( ) - > SetDirty ( true ) ;
}
// we are going to attempt to re-render the right-hand page
// sometimes, the book stutters on a page flip and screws up the book
if ( fCurrBook )
{
fCurrBook - > IRenderPage ( fCurrBook - > fCurrentPage + 1 , pfJournalDlgProc : : kTagRightDTMap ) ;
// move the videos over
fCurrBook - > IMoveMovies ( PageMaterial ( kTurnBackPage ) , PageMaterial ( kLeftPage ) ) ;
}
}
// Hide our page flipping button/checkbox/whatever
fTurnPageButton - > SetVisible ( false ) ;
// Update page corners
UpdatePageCorners ( kBothSides ) ;
fCurrentlyTurning = false ;
// Start our FX once the page is done turning
fResetSFXFlag = true ;
}
//// UpdatePageCorners //////////////////////////////////////////////////////
// Enables or disables the left and right page corners, to indicate current turnage state
void pfBookData : : UpdatePageCorners ( WhichSide which )
{
// make sure there is a book to update!
if ( fCurrBook )
{
if ( ! fCurrBook - > fAllowTurning | | ! fCurrBook - > fAreWeShowing )
{
fLeftCorner - > SetVisible ( false ) ;
fLeftCorner - > SetEnabled ( false ) ;
fRightCorner - > SetVisible ( false ) ;
fRightCorner - > SetEnabled ( false ) ;
return ;
}
if ( ( which = = kLeftSide ) | | ( which = = kBothSides ) )
{
if ( fCurrBook - > fAreEditing )
fLeftCorner - > SetVisible ( ! fLeftEditCtrl - > ShowingBeginningOfBuffer ( ) ) ; // only show if the control is not viewing the beginning of the buffer
else
fLeftCorner - > SetVisible ( ( fCurrBook - > fCurrentPage > = 2 ) ? true : false ) ;
// Note: always disabled (we just go on the page click itself)
fLeftCorner - > SetEnabled ( false ) ;
}
if ( ( which = = kRightSide ) | | ( which = = kBothSides ) )
{
if ( fCurrBook - > fAreEditing )
fRightCorner - > SetVisible ( ! fRightEditCtrl - > ShowingEndOfBuffer ( ) ) ; // only show if the control is not viewing the end of the buffer
else
fRightCorner - > SetVisible ( ( fCurrBook - > fCurrentPage + 2 < = fCurrBook - > fLastPage ) ? true : false ) ;
fRightCorner - > SetEnabled ( false ) ;
}
}
}
//// SetCurrSize ////////////////////////////////////////////////////////////
// Seeks the width and height animations to set the desired book size. Sizes are in % across the animation
void pfBookData : : SetCurrSize ( float w , float h )
{
fWidthCtrl - > SetCurrValue ( w ) ;
fHeightCtrl - > SetCurrValue ( h ) ;
}
//// PlayBookCloseAnim //////////////////////////////////////////////////////
// Triggers our animation for closing or opening the book.
void pfBookData : : PlayBookCloseAnim ( bool closeIt /*= true*/ , bool immediate /*= false*/ )
{
// Disable the book cover button if we're opening, enable otherwise
fCoverButton - > SetEnabled ( closeIt ) ;
// Tell our cover button to check or uncheck
fCoverButton - > SetChecked ( ! closeIt , immediate ) ;
// Trigger the open (or close) sound
if ( ! immediate )
fCoverButton - > PlaySound ( closeIt ? pfGUICheckBoxCtrl : : kMouseUp : pfGUICheckBoxCtrl : : kMouseDown ) ;
fCurrentlyOpen = ! closeIt ;
}
//// Event routines from a linked multi-line edit control ////////////////////
void pfBookData : : HitEndOfControlList ( int32_t cursorPos )
{
fAdjustCursorTo = cursorPos ;
if ( fCurrBook )
fCurrBook - > NextPage ( ) ;
}
void pfBookData : : HitBeginningOfControlList ( int32_t cursorPos )
{
fAdjustCursorTo = cursorPos ;
if ( fCurrBook )
fCurrBook - > PreviousPage ( ) ;
}
void pfBookData : : EnableEditGUI ( bool enable /* =true */ )
{
if ( fEditable )
{
fLeftEditCtrl - > SetEnabled ( enable ) ;
fLeftEditCtrl - > SetVisible ( enable ) ;
fRightEditCtrl - > SetEnabled ( enable ) ;
fRightEditCtrl - > SetVisible ( enable ) ;
// we don't make these editable because they are temps used for the turning page
fTurnFrontEditCtrl - > SetVisible ( false ) ; // we don't want these visible initially either
fTurnBackEditCtrl - > SetVisible ( false ) ;
}
}
//// Our Singleton Stuff /////////////////////////////////////////////////////
//pfJournalBook *pfJournalBook::fInstance = nil;
std : : map < plString , pfBookData * > pfJournalBook : : fBookGUIs ;
void pfJournalBook : : SingletonInit ( void )
{
fBookGUIs [ " BkBook " ] = new pfBookData ( ) ; // load the default book data object
hsgResMgr : : ResMgr ( ) - > NewKey ( " BkBook " , fBookGUIs [ " BkBook " ] , pfGameGUIMgr : : GetInstance ( ) - > GetKey ( ) - > GetUoid ( ) . GetLocation ( ) ) ;
fBookGUIs [ " BkBook " ] - > LoadGUI ( ) ;
}
void pfJournalBook : : SingletonShutdown ( void )
{
std : : map < plString , pfBookData * > : : iterator i = fBookGUIs . begin ( ) ;
while ( i ! = fBookGUIs . end ( ) )
{
pfBookData * bookData = i - > second ;
bookData - > GetKey ( ) - > UnRefObject ( ) ;
i - > second = nil ;
i + + ;
}
fBookGUIs . clear ( ) ;
}
void pfJournalBook : : LoadGUI ( const plString & guiName )
{
if ( fBookGUIs . find ( guiName ) = = fBookGUIs . end ( ) ) // is it already loaded?
{ // nope, load it
fBookGUIs [ guiName ] = new pfBookData ( guiName ) ;
hsgResMgr : : ResMgr ( ) - > NewKey ( guiName , fBookGUIs [ guiName ] , pfGameGUIMgr : : GetInstance ( ) - > GetKey ( ) - > GetUoid ( ) . GetLocation ( ) ) ;
fBookGUIs [ guiName ] - > LoadGUI ( ) ;
}
}
void pfJournalBook : : UnloadGUI ( const plString & guiName )
{
if ( guiName . Compare ( " BkBook " ) = = 0 )
return ; // do not allow people to unload the default book gui
std : : map < plString , pfBookData * > : : iterator loc = fBookGUIs . find ( guiName ) ;
if ( loc ! = fBookGUIs . end ( ) ) // make sure it's loaded
{
fBookGUIs [ guiName ] - > GetKey ( ) - > UnRefObject ( ) ;
fBookGUIs [ guiName ] = nil ;
fBookGUIs . erase ( loc ) ;
}
}
void pfJournalBook : : UnloadAllGUIs ( )
{
std : : map < plString , pfBookData * > : : iterator i = fBookGUIs . begin ( ) ;
std : : vector < plString > names ;
while ( i ! = fBookGUIs . end ( ) )
{
plString name = i - > first ;
names . push_back ( name ) ; // store a list of keys
i + + ;
}
int idx ;
for ( idx = 0 ; idx < names . size ( ) ; idx + + )
UnloadGUI ( names [ idx ] ) ; // UnloadGUI won't unload BkBook
}
//// Constructor /////////////////////////////////////////////////////////////
// The constructor takes in the esHTML source for the journal, along with
// the name of the mipmap to use as the cover of the book. The callback
// key is the keyed object to send event messages to (see <img> tag).
pfJournalBook : : pfJournalBook ( const char * esHTMLSource , plKey coverImageKey , plKey callbackKey /*= nil*/ ,
const plLocation & hintLoc /* = plLocation::kGlobalFixedLoc */ , const plString & guiName /* = nil */ )
{
if ( ! guiName . IsEmpty ( ) )
fCurBookGUI = guiName ;
else
fCurBookGUI = " BkBook " ;
if ( fBookGUIs . find ( fCurBookGUI ) = = fBookGUIs . end ( ) )
{
fBookGUIs [ fCurBookGUI ] = new pfBookData ( fCurBookGUI ) ;
hsgResMgr : : ResMgr ( ) - > NewKey ( fCurBookGUI , fBookGUIs [ fCurBookGUI ] , pfGameGUIMgr : : GetInstance ( ) - > GetKey ( ) - > GetUoid ( ) . GetLocation ( ) ) ;
fBookGUIs [ fCurBookGUI ] - > LoadGUI ( ) ;
}
fCurrentPage = 0 ;
fLastPage = - 1 ;
fCoverMipKey = coverImageKey ;
fCoverFromHTML = false ;
fCallbackKey = callbackKey ;
fWidthScale = fHeightScale = 0.f ;
fPageTMargin = fPageLMargin = fPageBMargin = fPageRMargin = 16 ;
fAllowTurning = true ;
fAreWeShowing = false ;
fCoverTint . Set ( 0.f , 0.f , 0.f , 1.f ) ;
fTintFirst = true ;
fTintCover = false ;
fAreEditing = false ;
fWantEditing = false ;
fDefLoc = hintLoc ;
wchar_t * wESHTMLSource = hsStringToWString ( esHTMLSource ) ;
fUncompiledSource = wESHTMLSource ;
ICompileSource ( wESHTMLSource , hintLoc ) ;
delete [ ] wESHTMLSource ;
}
pfJournalBook : : pfJournalBook ( const wchar_t * esHTMLSource , plKey coverImageKey , plKey callbackKey /*= nil*/ ,
const plLocation & hintLoc /* = plLocation::kGlobalFixedLoc */ , const plString & guiName /* = nil */ )
{
if ( ! guiName . IsEmpty ( ) )
fCurBookGUI = guiName ;
else
fCurBookGUI = " BkBook " ;
if ( fBookGUIs . find ( fCurBookGUI ) = = fBookGUIs . end ( ) )
{
fBookGUIs [ fCurBookGUI ] = new pfBookData ( fCurBookGUI ) ;
hsgResMgr : : ResMgr ( ) - > NewKey ( fCurBookGUI , fBookGUIs [ fCurBookGUI ] , pfGameGUIMgr : : GetInstance ( ) - > GetKey ( ) - > GetUoid ( ) . GetLocation ( ) ) ;
fBookGUIs [ fCurBookGUI ] - > LoadGUI ( ) ;
}
fCurrentPage = 0 ;
fLastPage = - 1 ;
fCoverMipKey = coverImageKey ;
fCoverFromHTML = false ;
fCallbackKey = callbackKey ;
fWidthScale = fHeightScale = 0.f ;
fPageTMargin = fPageLMargin = fPageBMargin = fPageRMargin = 16 ;
fAllowTurning = true ;
fAreWeShowing = false ;
fCoverTint . Set ( 1.f , 1.f , 1.f , 1.f ) ;
fTintFirst = true ;
fTintCover = false ;
fAreEditing = false ;
fWantEditing = false ;
fDefLoc = hintLoc ;
fUncompiledSource = esHTMLSource ;
ICompileSource ( esHTMLSource , hintLoc ) ;
}
pfJournalBook : : ~ pfJournalBook ( )
{
if ( fBookGUIs . find ( fCurBookGUI ) ! = fBookGUIs . end ( ) ) // it might have been deleted before we got here
if ( fBookGUIs [ fCurBookGUI ] & & fBookGUIs [ fCurBookGUI ] - > CurBook ( ) = = this )
Hide ( ) ;
IFreeSource ( ) ;
}
//// MsgReceive //////////////////////////////////////////////////////////////
bool pfJournalBook : : MsgReceive ( plMessage * pMsg )
{
return hsKeyedObject : : MsgReceive ( pMsg ) ;
}
void pfJournalBook : : SetGUI ( const plString & guiName )
{
if ( ! guiName . IsEmpty ( ) )
fCurBookGUI = guiName ;
if ( fBookGUIs . find ( fCurBookGUI ) = = fBookGUIs . end ( ) )
fCurBookGUI = " BkBook " ; // requested GUI isn't loaded, so use default GUI
SetEditable ( fWantEditing ) ; // make sure that if we want editing, to set it
ICompileSource ( fUncompiledSource . c_str ( ) , fDefLoc ) ; // recompile the source to be safe
}
//// Show ////////////////////////////////////////////////////////////////////
// Shows the book, optionally starting open or closed
void pfJournalBook : : Show ( bool startOpened /*= false */ )
{
fBookGUIs [ fCurBookGUI ] - > StartedOpen ( startOpened ) ;
fBookGUIs [ fCurBookGUI ] - > CurBook ( this ) ;
fBookGUIs [ fCurBookGUI ] - > SetCurrSize ( fWidthScale , fHeightScale ) ;
ILoadAllImages ( false ) ;
hsGMaterial * cover = fBookGUIs [ fCurBookGUI ] - > CoverMaterial ( ) ;
if ( cover ! = nil )
{
hsTArray < plLayerInterface * > layers ;
plMipmap * mip = fCoverMipKey ? plMipmap : : ConvertNoRef ( fCoverMipKey - > ObjectIsLoaded ( ) ) : nil ;
if ( mip ! = nil )
{
layers . Append ( IMakeBaseLayer ( mip ) ) ;
int i ;
for ( i = 0 ; i < fCoverDecals . GetCount ( ) ; i + + )
{
if ( fCoverDecals [ i ] - > fType = = pfEsHTMLChunk : : kDecal )
{
plMipmap * decal = plMipmap : : ConvertNoRef ( fCoverDecals [ i ] - > fImageKey ! = nil ? fCoverDecals [ i ] - > fImageKey - > ObjectIsLoaded ( ) : nil ) ;
if ( decal ! = nil )
layers . Append ( IMakeDecalLayer ( fCoverDecals [ i ] , decal , mip ) ) ;
}
else
{
// it's a cover movie, not a decal, so we make a layer, thinking it's at 0,0 and a left map (which gives us the results we want)
plLayerAVI * movieLayer = IMakeMovieLayer ( fCoverDecals [ i ] , 0 , 0 , mip , pfJournalDlgProc : : kTagLeftDTMap , false ) ;
loadedMovie * movie = new loadedMovie ;
movie - > movieLayer = movieLayer ;
movie - > movieChunk = fCoverDecals [ i ] ;
fLoadedMovies . Append ( movie ) ;
layers . Append ( plLayerInterface : : ConvertNoRef ( movieLayer ) ) ;
fVisibleLinks . Reset ( ) ; // remove any links that the make movie layer might have added, since a cover movie can't link
}
}
ISetDecalLayers ( cover , layers ) ;
}
else
{
layers . Append ( IMakeBaseLayer ( fBookGUIs [ fCurBookGUI ] - > DefaultCover ( ) ) ) ;
ISetDecalLayers ( cover , layers ) ;
}
// release our ref on the cover layers since the material will take care of them now
int i ;
for ( i = 0 ; i < layers . GetCount ( ) ; i + + )
GetKey ( ) - > Release ( layers [ i ] - > GetKey ( ) ) ;
}
// fInstance->IPlayBookCloseAnim( !startOpened, true );
fBookGUIs [ fCurBookGUI ] - > TurnPageButton ( ) - > SetVisible ( false ) ;
ITriggerCloseWithNotify ( ! startOpened , true ) ;
}
//// IFinishShow /////////////////////////////////////////////////////////////
// Finish showing the book, due to the animation being done seeking
void pfJournalBook : : IFinishShow ( bool startOpened )
{
fBookGUIs [ fCurBookGUI ] - > Dialog ( ) - > Show ( ) ;
fAreWeShowing = true ;
if ( startOpened )
{
// Render initial pages
fCurrentPage = 0 ;
fVisibleLinks . Reset ( ) ;
IRenderPage ( 0 , pfJournalDlgProc : : kTagLeftDTMap ) ;
IRenderPage ( 1 , pfJournalDlgProc : : kTagRightDTMap ) ;
fBookGUIs [ fCurBookGUI ] - > UpdatePageCorners ( pfBookData : : kBothSides ) ;
}
ISendNotify ( kNotifyShow ) ;
}
//// Hide ////////////////////////////////////////////////////////////////////
void pfJournalBook : : Hide ( void )
{
if ( fBookGUIs [ fCurBookGUI ] )
{
// we can only hide our own dialog
if ( fBookGUIs [ fCurBookGUI ] - > CurBook ( ) = = this )
{
if ( fBookGUIs [ fCurBookGUI ] - > Dialog ( ) )
fBookGUIs [ fCurBookGUI ] - > Dialog ( ) - > Hide ( ) ;
fBookGUIs [ fCurBookGUI ] - > CurBook ( nil ) ;
ISendNotify ( kNotifyHide ) ;
ILoadAllImages ( true ) ;
// purge the dynaTextMaps, we're done with them for now
IPurgeDynaTextMaps ( ) ;
// nuke the movies so they don't stay in memory (they're big!)
int i ;
for ( i = 0 ; i < fLoadedMovies . GetCount ( ) ; i + + )
{
plLayerAVI * movie = fLoadedMovies [ i ] - > movieLayer ;
movie - > GetKey ( ) - > UnRefObject ( ) ;
delete fLoadedMovies [ i ] ;
}
fLoadedMovies . Reset ( ) ;
}
}
}
//// Open ////////////////////////////////////////////////////////////////////
// Opens the book, optionally to the given page
void pfJournalBook : : Open ( uint32_t startingPage /*= 0 */ )
{
if ( ! fBookGUIs [ fCurBookGUI ] - > CurrentlyOpen ( ) )
{
fBookGUIs [ fCurBookGUI ] - > PlayBookCloseAnim ( false ) ;
// Render initial pages
fCurrentPage = startingPage ;
fVisibleLinks . Reset ( ) ;
IRenderPage ( startingPage , pfJournalDlgProc : : kTagLeftDTMap ) ;
IRenderPage ( startingPage + 1 , pfJournalDlgProc : : kTagRightDTMap ) ;
fBookGUIs [ fCurBookGUI ] - > UpdatePageCorners ( pfBookData : : kBothSides ) ;
}
}
//// Close ///////////////////////////////////////////////////////////////////
// Closes the book.
void pfJournalBook : : Close ( void )
{
// don't allow them to close the book if the book started open
if ( ! fBookGUIs [ fCurBookGUI ] - > StartedOpen ( ) & & fBookGUIs [ fCurBookGUI ] - > CurrentlyOpen ( ) )
{
ISendNotify ( kNotifyClose ) ;
fBookGUIs [ fCurBookGUI ] - > PlayBookCloseAnim ( true ) ;
}
}
//// CloseAndHide ////////////////////////////////////////////////////////////
// Closes the book, then calls Hide() once it's done closing
void pfJournalBook : : CloseAndHide ( void )
{
// if they start with the book open, then don't allow them to close it
if ( ! fBookGUIs [ fCurBookGUI ] - > StartedOpen ( ) & & fBookGUIs [ fCurBookGUI ] - > CurrentlyOpen ( ) )
{
// if we are flipping a book then kill the page flipping animation
if ( fBookGUIs [ fCurBookGUI ] - > CurrentlyTurning ( ) )
fBookGUIs [ fCurBookGUI ] - > KillPageFlip ( ) ;
ISendNotify ( kNotifyClose ) ;
ITriggerCloseWithNotify ( true , false ) ;
// Don't hide until we get the callback!
}
else
// Already closed, just hide
Hide ( ) ;
}
//// ITriggerCloseWithNotify /////////////////////////////////////////////////
// Close with a notify
void pfJournalBook : : ITriggerCloseWithNotify ( bool closeNotOpen , bool immediate )
{
// Disable the book cover button if we're opening, enable otherwise
fBookGUIs [ fCurBookGUI ] - > CoverButton ( ) - > SetEnabled ( closeNotOpen ) ;
// Do the animation manually so we can get a callback
fBookGUIs [ fCurBookGUI ] - > CurrentlyOpen ( ! closeNotOpen ) ;
const hsTArray < plKey > & keys = fBookGUIs [ fCurBookGUI ] - > CoverButton ( ) - > GetAnimationKeys ( ) ;
plString animName = fBookGUIs [ fCurBookGUI ] - > CoverButton ( ) - > GetAnimationName ( ) ;
plAnimCmdMsg * msg = new plAnimCmdMsg ( ) ;
if ( ! immediate )
{
msg - > SetCmd ( plAnimCmdMsg : : kContinue ) ;
msg - > SetCmd ( closeNotOpen ? plAnimCmdMsg : : kSetBackwards : plAnimCmdMsg : : kSetForewards ) ;
}
else
{
msg - > SetCmd ( plAnimCmdMsg : : kStop ) ;
msg - > SetCmd ( closeNotOpen ? plAnimCmdMsg : : kGoToBegin : plAnimCmdMsg : : kGoToEnd ) ;
}
msg - > SetAnimName ( animName ) ;
msg - > AddReceivers ( keys ) ;
plEventCallbackMsg * eventMsg = new plEventCallbackMsg ;
eventMsg - > AddReceiver ( fBookGUIs [ fCurBookGUI ] - > GetKey ( ) ) ;
eventMsg - > fRepeats = 0 ;
eventMsg - > fUser = 0x08 | ( closeNotOpen ? 0x00 : 0x01 ) ; // So we know which this is for
eventMsg - > fEvent = immediate ? ( kSingleFrameEval ) : kStop ;
msg - > SetCmd ( plAnimCmdMsg : : kAddCallbacks ) ;
msg - > AddCallback ( eventMsg ) ;
hsRefCnt_SafeUnRef ( eventMsg ) ;
msg - > Send ( ) ;
// Trigger the open (or close) sound
if ( ! immediate )
fBookGUIs [ fCurBookGUI ] - > CoverButton ( ) - > PlaySound ( closeNotOpen ? pfGUICheckBoxCtrl : : kMouseUp : pfGUICheckBoxCtrl : : kMouseDown ) ;
}
//// NextPage ////////////////////////////////////////////////////////////////
// Advances forward one page
void pfJournalBook : : NextPage ( void )
{
if ( ( fBookGUIs [ fCurBookGUI ] - > CurrentlyTurning ( ) ) | | ( ! fAllowTurning ) | | ( ! fAreWeShowing ) )
return ;
if ( ( fAreEditing ) & & ! ( fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) - > ShowingEndOfBuffer ( ) ) ) // we're editing the book, so page turning is different here
{
fCurrentPage + = 2 ; // we just go to the next page, an editable book has "infinite" pages anyway
pfGUIMultiLineEditCtrl * right = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) ;
pfGUIMultiLineEditCtrl * left = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) ;
pfGUIMultiLineEditCtrl * turnFront = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) ;
pfGUIMultiLineEditCtrl * turnBack = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) ;
// re-link the controls with the turn page in the middle
left - > SetNext ( turnFront ) ;
turnBack - > SetNext ( right ) ;
// At this point, only the left and right pages are visible, we don't want to actually update anything until the animation
// starts so that nothing flashes or overdraws.
fBookGUIs [ fCurBookGUI ] - > StartTriggeredFlip ( false ) ;
fBookGUIs [ fCurBookGUI ] - > TurnPageButton ( ) - > PlaySound ( pfGUICheckBoxCtrl : : kMouseUp ) ;
ISendNotify ( kNotifyNextPage ) ;
}
else if ( fCurrentPage + 2 < = fLastPage )
{
fCurrentPage + = 2 ;
fVisibleLinks . Reset ( ) ;
// Swap the right DT map into the turn page front DTMap, then render
// the new current page into turn page back and currPage+1 into
// the right DTMap
plDynamicTextMap * turnFront = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagTurnFrontDTMap ) ;
plDynamicTextMap * right = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagRightDTMap ) ;
if ( turnFront - > IsValid ( ) & & right - > IsValid ( ) )
{
memcpy ( turnFront - > GetImage ( ) , right - > GetImage ( ) , right - > GetLevelSize ( 0 ) ) ;
if ( turnFront - > GetDeviceRef ( ) ! = nil )
turnFront - > GetDeviceRef ( ) - > SetDirty ( true ) ;
}
// copy the videos over
IMoveMovies ( fBookGUIs [ fCurBookGUI ] - > PageMaterial ( pfBookData : : kRightPage ) , fBookGUIs [ fCurBookGUI ] - > PageMaterial ( pfBookData : : kTurnFrontPage ) ) ;
IRenderPage ( fCurrentPage , pfJournalDlgProc : : kTagTurnBackDTMap ) ;
// This will fire a callback when it's done that'll let us continue the setup
fBookGUIs [ fCurBookGUI ] - > StartTriggeredFlip ( false ) ;
// Play us a sound too, if defined on our button
fBookGUIs [ fCurBookGUI ] - > TurnPageButton ( ) - > PlaySound ( pfGUICheckBoxCtrl : : kMouseUp ) ;
ISendNotify ( kNotifyNextPage ) ;
}
}
//// PreviousPage ////////////////////////////////////////////////////////////
// Same, only back
void pfJournalBook : : PreviousPage ( void )
{
if ( ( fBookGUIs [ fCurBookGUI ] - > CurrentlyTurning ( ) ) | | ( ! fAllowTurning ) )
return ;
if ( fAreEditing ) // we're editing the book, so page turning is different here
{
if ( ! ( fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) - > ShowingBeginningOfBuffer ( ) ) ) // make sure we don't flip past the beginning
{
if ( fCurrentPage > = 2 ) // this variable can get out of whack if we open the book to a page in the middle
fCurrentPage - = 2 ; // just making sure that this doesn't go below zero (and therefore wrap around)
pfGUIMultiLineEditCtrl * right = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) ;
pfGUIMultiLineEditCtrl * left = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) ;
pfGUIMultiLineEditCtrl * turnFront = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) ;
pfGUIMultiLineEditCtrl * turnBack = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) ;
// adjust the starting position of the left control to the new start
int32_t newStartLine = left - > GetFirstVisibleLine ( ) - ( ( left - > GetNumVisibleLines ( ) - 1 ) * 2 ) ;
left - > SetGlobalStartLine ( newStartLine ) ;
// re-link the controls with the turn page in the middle
left - > SetNext ( turnFront ) ;
turnBack - > SetNext ( right ) ;
// At this point, only the left and right pages are visible, we don't want to actually update anything until the animation
// starts so that nothing flashes or overdraws.
fBookGUIs [ fCurBookGUI ] - > StartTriggeredFlip ( true ) ;
fBookGUIs [ fCurBookGUI ] - > TurnPageButton ( ) - > PlaySound ( pfGUICheckBoxCtrl : : kMouseUp ) ;
ISendNotify ( kNotifyPreviousPage ) ;
}
else
{
Close ( ) ;
}
}
else if ( fCurrentPage > 1 )
{
fCurrentPage - = 2 ;
fVisibleLinks . Reset ( ) ;
// Swap the left DT map into the turn page back DTMap, then render
// the new current page into the left and currPage+1 into
// the turn page front DTMap
plDynamicTextMap * turnBack = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagTurnBackDTMap ) ;
plDynamicTextMap * left = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagLeftDTMap ) ;
if ( turnBack - > IsValid ( ) & & left - > IsValid ( ) )
{
memcpy ( turnBack - > GetImage ( ) , left - > GetImage ( ) , left - > GetLevelSize ( 0 ) ) ;
if ( turnBack - > GetDeviceRef ( ) ! = nil )
turnBack - > GetDeviceRef ( ) - > SetDirty ( true ) ;
}
// copy the videos over
IMoveMovies ( fBookGUIs [ fCurBookGUI ] - > PageMaterial ( pfBookData : : kLeftPage ) , fBookGUIs [ fCurBookGUI ] - > PageMaterial ( pfBookData : : kTurnBackPage ) ) ;
IRenderPage ( fCurrentPage + 1 , pfJournalDlgProc : : kTagTurnFrontDTMap ) ;
// This will fire a callback when it's done that'll let us continue the setup
fBookGUIs [ fCurBookGUI ] - > StartTriggeredFlip ( true ) ;
// Play us a sound too, if defined on our button
fBookGUIs [ fCurBookGUI ] - > TurnPageButton ( ) - > PlaySound ( pfGUICheckBoxCtrl : : kMouseUp ) ;
ISendNotify ( kNotifyPreviousPage ) ;
}
else
{
Close ( ) ;
}
}
//// IFindCurrVisibleLink ////////////////////////////////////////////////////
// Find the current moused link, if any
int32_t pfJournalBook : : IFindCurrVisibleLink ( bool rightNotLeft , bool hoverNotUp )
{
pfGUIClickMapCtrl * ctrl = ( rightNotLeft ) ? fBookGUIs [ fCurBookGUI ] - > RightPageMap ( ) : fBookGUIs [ fCurBookGUI ] - > LeftPageMap ( ) ;
hsPoint3 pt = hoverNotUp ? ctrl - > GetLastMousePt ( ) : ctrl - > GetLastMouseUpPt ( ) ;
// This should be 0-1 in the context of the control, so scale to our DTMap size
plDynamicTextMap * dtMap = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( rightNotLeft ? pfJournalDlgProc : : kTagRightDTMap : pfJournalDlgProc : : kTagLeftDTMap ) ;
pt . fX * = ( float ) dtMap - > GetWidth ( ) ;
pt . fY * = ( float ) dtMap - > GetHeight ( ) ;
if ( rightNotLeft )
{
// Clicks on the right side are offsetted in x by the left side's width
dtMap = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagLeftDTMap ) ;
pt . fX + = dtMap - > GetWidth ( ) ;
}
// Search through the list of visible hotspots
uint32_t i ;
for ( i = 0 ; i < fVisibleLinks . GetCount ( ) ; i + + )
{
if ( fVisibleLinks [ i ] - > fLinkRect . Contains ( ( int16_t ) pt . fX , ( int16_t ) pt . fY ) )
{
// Found a visible link
return ( int32_t ) i ;
}
}
return - 1 ;
}
//// IHandleLeftSideClick ////////////////////////////////////////////////////
void pfJournalBook : : IHandleLeftSideClick ( void )
{
if ( fBookGUIs [ fCurBookGUI ] - > CurrentlyTurning ( ) )
return ;
int32_t idx = IFindCurrVisibleLink ( false , false ) ;
if ( idx ! = - 1 )
{
if ( fVisibleLinks [ idx ] - > fFlags & pfEsHTMLChunk : : kActAsCB )
IHandleCheckClick ( idx , pfBookData : : kLeftSide ) ;
else
ISendNotify ( kNotifyImageLink , fVisibleLinks [ idx ] - > fEventID ) ;
return ;
}
// No link found that we're inside of, so just do the default behavior of turning the page
PreviousPage ( ) ;
}
void pfJournalBook : : IHandleRightSideClick ( void )
{
if ( fBookGUIs [ fCurBookGUI ] - > CurrentlyTurning ( ) )
return ;
int32_t idx = IFindCurrVisibleLink ( true , false ) ;
if ( idx ! = - 1 )
{
if ( fVisibleLinks [ idx ] - > fFlags & pfEsHTMLChunk : : kActAsCB )
IHandleCheckClick ( idx , pfBookData : : kRightSide ) ;
else
ISendNotify ( kNotifyImageLink , fVisibleLinks [ idx ] - > fEventID ) ;
return ;
}
// No link found that we're inside of, so just do the default behavior of turning the page
NextPage ( ) ;
}
//// IHandleCheckClick ///////////////////////////////////////////////////////
// Process a click on the given "check box" image
void pfJournalBook : : IHandleCheckClick ( uint32_t idx , pfBookData : : WhichSide which )
{
// Special processing for checkboxes--toggle our state, switch our opacity
// and then send a notify about our new state
bool check = ( fVisibleLinks [ idx ] - > fFlags & pfEsHTMLChunk : : kChecked ) ? false : true ;
if ( check )
{
fVisibleLinks [ idx ] - > fFlags | = pfEsHTMLChunk : : kChecked ;
// fVisibleLinks[ idx ]->fCurrOpacity = fVisibleLinks[ idx ]->fMaxOpacity;
}
else
{
fVisibleLinks [ idx ] - > fFlags & = ~ pfEsHTMLChunk : : kChecked ;
// fVisibleLinks[ idx ]->fCurrOpacity = fVisibleLinks[ idx ]->fMinOpacity;
}
// Re-render the page we're on, to show the change in state
IRenderPage ( fCurrentPage + ( ( which = = pfBookData : : kLeftSide ) ? 0 : 1 ) , ( which = = pfBookData : : kLeftSide ) ? pfJournalDlgProc : : kTagLeftDTMap : pfJournalDlgProc : : kTagRightDTMap ) ;
ISendNotify ( check ? kNotifyImageLink : kNotifyCheckUnchecked , fVisibleLinks [ idx ] - > fEventID ) ;
// Register for FX processing, so we can fade the checkbox in
fBookGUIs [ fCurBookGUI ] - > RegisterForSFX ( ( pfBookData : : WhichSide ) ( fBookGUIs [ fCurBookGUI ] - > CurSFXPages ( ) | which ) ) ;
}
//// GoToPage ////////////////////////////////////////////////////////////////
// For completeness...
void pfJournalBook : : GoToPage ( uint32_t pageNumber )
{
// Put us here, but only on an even page (odd pages go on the right, y'know)
// (no need for a range check, going past the end simply puts you on a blank page, able to go backward but not forward)
fCurrentPage = pageNumber & ~ 0x00000001 ;
fVisibleLinks . Reset ( ) ;
IRenderPage ( fCurrentPage , pfJournalDlgProc : : kTagLeftDTMap ) ;
IRenderPage ( fCurrentPage + 1 , pfJournalDlgProc : : kTagRightDTMap ) ;
fBookGUIs [ fCurBookGUI ] - > UpdatePageCorners ( pfBookData : : kBothSides ) ;
}
//// SetEditable /////////////////////////////////////////////////////////////
void pfJournalBook : : SetEditable ( bool editable )
{
if ( fBookGUIs [ fCurBookGUI ] - > IsEditable ( ) ) // make sure this GUI supports editing
{
fBookGUIs [ fCurBookGUI ] - > EnableEditGUI ( editable ) ;
fAreEditing = editable ; // we may be editing the book, so change rendering/page flipping methods
if ( editable )
fLastPage = 0 ; // setting this to 0 since editable books don't know what the last page is (it always changes)
}
else
fWantEditing = editable ; // we want to edit, but the gui doesn't support it, check again if the GUI changes
} ;
//// ForceCacheCalculations //////////////////////////////////////////////////
// Just forces a full calc of the cached info
void pfJournalBook : : ForceCacheCalculations ( void )
{
// Make sure our page starts are up-to-snuff, at least to this point
IRecalcPageStarts ( - 1 ) ;
}
// Tiny helper to convert hex values the *right* way
static uint32_t IConvertHex ( const wchar_t * str )
{
uint32_t value = 0 ;
while ( * str ! = 0 )
{
value < < = 4 ;
switch ( * str )
{
case L ' 0 ' : value | = 0x0 ; break ;
case L ' 1 ' : value | = 0x1 ; break ;
case L ' 2 ' : value | = 0x2 ; break ;
case L ' 3 ' : value | = 0x3 ; break ;
case L ' 4 ' : value | = 0x4 ; break ;
case L ' 5 ' : value | = 0x5 ; break ;
case L ' 6 ' : value | = 0x6 ; break ;
case L ' 7 ' : value | = 0x7 ; break ;
case L ' 8 ' : value | = 0x8 ; break ;
case L ' 9 ' : value | = 0x9 ; break ;
case L ' a ' : case L ' A ' : value | = 0xa ; break ;
case L ' b ' : case L ' B ' : value | = 0xb ; break ;
case L ' c ' : case L ' C ' : value | = 0xc ; break ;
case L ' d ' : case L ' D ' : value | = 0xd ; break ;
case L ' e ' : case L ' E ' : value | = 0xe ; break ;
case L ' f ' : case L ' F ' : value | = 0xf ; break ;
}
str + + ;
}
return value ;
}
//// ICompileSource //////////////////////////////////////////////////////////
// Compiles the given string of esHTML source into our compiled chunk list
bool pfJournalBook : : ICompileSource ( const wchar_t * source , const plLocation & hintLoc )
{
IFreeSource ( ) ;
pfEsHTMLChunk * chunk , * lastParChunk = new pfEsHTMLChunk ( nil ) ;
const wchar_t * c , * start ;
wchar_t name [ 128 ] , option [ 256 ] ;
float bookWidth = 1.0 , bookHeight = 1.0 ;
uint8_t movieIndex = 0 ; // the index of a movie in the source (used for id purposes)
plKey anotherKey ;
// Parse our source!
for ( start = c = source ; * c ! = 0 ; )
{
// Are we on a tag?
uint8_t type = IGetTagType ( c ) ;
if ( type ! = pfEsHTMLChunk : : kEmpty )
{
// First, end the current paragraph chunk, which is a special case 'cause its
// text is defined outside the tag
if ( start = = c )
{
// No actual text, just delete
delete lastParChunk ;
lastParChunk = nil ;
}
else if ( lastParChunk ! = nil )
{
uint32_t count = ( ( uintptr_t ) c - ( uintptr_t ) start ) / 2 ; // wchar_t is 2 bytes
wchar_t * temp = new wchar_t [ count + 1 ] ;
wcsncpy ( temp , start , count ) ;
temp [ count ] = L ' \0 ' ;
lastParChunk - > fText = temp ;
delete [ ] temp ;
// Special case to remove any last trailing carriage return
// if( count > 1 && lastParChunk->fText[ count - 1 ] == '\n' )
// lastParChunk->fText[ count - 1 ] = 0;
fHTMLSource . Append ( lastParChunk ) ;
}
// What chunk are we making now?
switch ( type )
{
case pfEsHTMLChunk : : kParagraph :
c + = 2 ;
chunk = new pfEsHTMLChunk ( nil ) ;
chunk - > fFlags = IFindLastAlignment ( ) ;
while ( IGetNextOption ( c , name , option ) )
{
if ( wcsicmp ( name , L " align " ) = = 0 )
{
if ( wcsicmp ( option , L " left " ) = = 0 )
chunk - > fFlags = pfEsHTMLChunk : : kLeft ;
else if ( wcsicmp ( option , L " center " ) = = 0 )
chunk - > fFlags = pfEsHTMLChunk : : kCenter ;
else if ( wcsicmp ( option , L " right " ) = = 0 )
chunk - > fFlags = pfEsHTMLChunk : : kRight ;
}
}
// Append text to this one (don't add to source just yet)
lastParChunk = chunk ;
break ;
case pfEsHTMLChunk : : kImage :
c + = 4 ;
chunk = new pfEsHTMLChunk ( nil , 0 ) ;
while ( IGetNextOption ( c , name , option ) )
{
if ( wcsicmp ( name , L " align " ) = = 0 )
{
chunk - > fFlags & = ~ pfEsHTMLChunk : : kAlignMask ;
if ( wcsicmp ( option , L " left " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kLeft ;
else if ( wcsicmp ( option , L " center " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kCenter ;
else if ( wcsicmp ( option , L " right " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kRight ;
}
else if ( wcsicmp ( name , L " src " ) = = 0 )
{
// Name of mipmap source
chunk - > fImageKey = IGetMipmapKey ( option , hintLoc ) ;
}
else if ( wcsicmp ( name , L " link " ) = = 0 )
{
chunk - > fEventID = wcstol ( option , NULL , 0 ) ;
chunk - > fFlags | = pfEsHTMLChunk : : kCanLink ;
}
else if ( wcsicmp ( name , L " blend " ) = = 0 )
{
if ( wcsicmp ( option , L " alpha " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kBlendAlpha ;
}
else if ( wcsicmp ( name , L " pos " ) = = 0 )
{
chunk - > fFlags | = pfEsHTMLChunk : : kFloating ;
wchar_t * comma = wcschr ( option , L ' , ' ) ;
if ( comma ! = nil )
{
chunk - > fAbsoluteY = wcstol ( comma + 1 , NULL , 0 ) ;
* comma = 0 ;
}
chunk - > fAbsoluteX = wcstol ( option , NULL , 0 ) ;
}
else if ( wcsicmp ( name , L " glow " ) = = 0 )
{
chunk - > fFlags | = pfEsHTMLChunk : : kGlowing ;
chunk - > fFlags & = ~ pfEsHTMLChunk : : kActAsCB ;
char * cOption = hsWStringToString ( option ) ;
char * comma = strchr ( cOption , ' , ' ) ;
if ( comma ! = nil )
{
char * comma2 = strchr ( comma + 1 , ' , ' ) ;
if ( comma2 ! = nil )
{
chunk - > fMaxOpacity = ( float ) atof ( comma2 + 1 ) ;
* comma2 = 0 ;
}
chunk - > fMinOpacity = ( float ) atof ( comma + 1 ) ;
* comma = 0 ;
}
chunk - > fSFXTime = ( float ) atof ( cOption ) ;
delete [ ] cOption ;
}
else if ( wcsicmp ( name , L " opacity " ) = = 0 )
{
chunk - > fFlags | = pfEsHTMLChunk : : kTranslucent ;
char * cOption = hsWStringToString ( option ) ;
chunk - > fCurrOpacity = ( float ) atof ( cOption ) ;
delete [ ] cOption ;
}
else if ( wcsicmp ( name , L " check " ) = = 0 )
{
chunk - > fFlags | = pfEsHTMLChunk : : kActAsCB ;
chunk - > fFlags & = ~ pfEsHTMLChunk : : kGlowing ;
wchar_t * comma = wcschr ( option , L ' , ' ) ;
if ( comma ! = nil )
{
wchar_t * comma2 = wcschr ( comma + 1 , L ' , ' ) ;
if ( comma2 ! = nil )
{
if ( wcstol ( comma2 + 1 , NULL , 0 ) ! = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kChecked ;
* comma2 = 0 ;
}
uint32_t c = IConvertHex ( comma + 1 ) ;
if ( wcslen ( comma + 1 ) < = 6 )
c | = 0xff000000 ; // Add in full alpha if none specified
chunk - > fOffColor . FromARGB32 ( c ) ;
* comma = 0 ;
}
uint32_t c = IConvertHex ( option ) ;
if ( wcslen ( option ) < = 6 )
c | = 0xff000000 ; // Add in full alpha if none specified
chunk - > fOnColor . FromARGB32 ( c ) ;
if ( chunk - > fFlags & pfEsHTMLChunk : : kChecked )
chunk - > fCurrColor = chunk - > fOnColor ;
else
chunk - > fCurrColor = chunk - > fOffColor ;
}
else if ( wcsicmp ( name , L " resize " ) = = 0 )
{
if ( wcsicmp ( option , L " no " ) = = 0 )
chunk - > fNoResizeImg = true ;
}
}
if ( chunk - > fImageKey ! = nil )
fHTMLSource . Append ( chunk ) ;
else
delete chunk ;
// Start new paragraph chunk after this one
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
case pfEsHTMLChunk : : kCover :
// Don't create an actual chunk for this one, just use the "src" and
// grab the mipmap key for our cover
c + = 6 ;
while ( IGetNextOption ( c , name , option ) )
{
if ( wcsicmp ( name , L " src " ) = = 0 )
{
// Name of mipmap source
anotherKey = IGetMipmapKey ( option , hintLoc ) ;
if ( anotherKey ! = nil )
{
fCoverMipKey = anotherKey ;
fCoverFromHTML = true ;
}
}
if ( wcsicmp ( name , L " tint " ) = = 0 )
{
fTintCover = true ;
fCoverTint . FromARGB32 ( wcstol ( option , nil , 16 ) | 0xff000000 ) ;
}
if ( wcsicmp ( name , L " tintfirst " ) = = 0 )
{
if ( wcsicmp ( option , L " no " ) = = 0 )
fTintFirst = false ;
}
}
// Still gotta create a new par chunk
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
case pfEsHTMLChunk : : kPageBreak :
c + = 3 ;
chunk = new pfEsHTMLChunk ( ) ;
while ( IGetNextOption ( c , name , option ) )
{
}
fHTMLSource . Append ( chunk ) ;
// Start new paragraph chunk after this one
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
case pfEsHTMLChunk : : kFontChange :
c + = 5 ;
chunk = new pfEsHTMLChunk ( nil , 0 , 0 ) ;
while ( IGetNextOption ( c , name , option ) )
{
if ( wcsicmp ( name , L " style " ) = = 0 )
{
uint8_t guiFlags = 0 ;
if ( wcsicmp ( option , L " b " ) = = 0 )
{
chunk - > fFlags = pfEsHTMLChunk : : kFontBold ;
guiFlags = plDynamicTextMap : : kFontBold ;
}
else if ( wcsicmp ( option , L " i " ) = = 0 )
{
chunk - > fFlags = pfEsHTMLChunk : : kFontItalic ;
guiFlags = plDynamicTextMap : : kFontItalic ;
}
else if ( wcsicmp ( option , L " bi " ) = = 0 )
{
chunk - > fFlags = pfEsHTMLChunk : : kFontBold | pfEsHTMLChunk : : kFontItalic ;
guiFlags = plDynamicTextMap : : kFontBold | plDynamicTextMap : : kFontItalic ;
}
else
chunk - > fFlags = pfEsHTMLChunk : : kFontRegular ;
if ( fBookGUIs [ fCurBookGUI ] - > IsEditable ( ) )
{
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) - > SetFontStyle ( guiFlags ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) - > SetFontStyle ( guiFlags ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) - > SetFontStyle ( guiFlags ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) - > SetFontStyle ( guiFlags ) ;
}
}
else if ( wcsicmp ( name , L " face " ) = = 0 )
{
// Name of mipmap source
chunk - > fText = option ;
if ( fBookGUIs [ fCurBookGUI ] - > IsEditable ( ) )
{
char * fontFace = hsWStringToString ( option ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) - > SetFontFace ( fontFace ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) - > SetFontFace ( fontFace ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) - > SetFontFace ( fontFace ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) - > SetFontFace ( fontFace ) ;
delete [ ] fontFace ;
}
}
else if ( wcsicmp ( name , L " size " ) = = 0 )
{
chunk - > fFontSize = wcstol ( option , NULL , 0 ) ;
if ( fBookGUIs [ fCurBookGUI ] - > IsEditable ( ) )
{
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) - > SetFontSize ( chunk - > fFontSize ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) - > SetFontSize ( chunk - > fFontSize ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) - > SetFontSize ( chunk - > fFontSize ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) - > SetFontSize ( chunk - > fFontSize ) ;
}
}
else if ( wcsicmp ( name , L " color " ) = = 0 )
{
chunk - > fColor . FromARGB32 ( wcstol ( option , nil , 16 ) | 0xff000000 ) ;
chunk - > fFlags | = pfEsHTMLChunk : : kFontColor ;
if ( fBookGUIs [ fCurBookGUI ] - > IsEditable ( ) )
{
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) - > SetFontColor ( chunk - > fColor ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) - > SetFontColor ( chunk - > fColor ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) - > SetFontColor ( chunk - > fColor ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) - > SetFontColor ( chunk - > fColor ) ;
}
}
else if ( wcsicmp ( name , L " spacing " ) = = 0 )
{
chunk - > fLineSpacing = wcstol ( option , NULL , 0 ) ;
chunk - > fFlags | = pfEsHTMLChunk : : kFontSpacing ;
}
}
fHTMLSource . Append ( chunk ) ;
// Start new paragraph chunk after this one
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
case pfEsHTMLChunk : : kMargin :
c + = 7 ;
while ( IGetNextOption ( c , name , option ) )
{
if ( wcsicmp ( name , L " top " ) = = 0 )
fPageTMargin = wcstol ( option , NULL , 0 ) ;
else if ( wcsicmp ( name , L " left " ) = = 0 )
fPageLMargin = wcstol ( option , NULL , 0 ) ;
else if ( wcsicmp ( name , L " bottom " ) = = 0 )
fPageBMargin = wcstol ( option , NULL , 0 ) ;
else if ( wcsicmp ( name , L " right " ) = = 0 )
fPageRMargin = wcstol ( option , NULL , 0 ) ;
}
// set the edit controls to the margins we just set
if ( fBookGUIs [ fCurBookGUI ] - > IsEditable ( ) )
{
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) - > SetMargins ( fPageTMargin , fPageLMargin , fPageBMargin , fPageRMargin ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) - > SetMargins ( fPageTMargin , fPageLMargin , fPageBMargin , fPageRMargin ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) - > SetMargins ( fPageTMargin , fPageLMargin , fPageBMargin , fPageRMargin ) ;
fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) - > SetMargins ( fPageTMargin , fPageLMargin , fPageBMargin , fPageRMargin ) ;
}
// Start a new paragraph chunk after this one
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
case pfEsHTMLChunk : : kBook :
c + = 5 ;
// don't actually create a chunk, just set the book size
while ( IGetNextOption ( c , name , option ) )
{
if ( wcsicmp ( name , L " height " ) = = 0 )
{
char * temp = hsWStringToString ( option ) ;
bookHeight = ( float ) atof ( temp ) ;
delete [ ] temp ;
}
else if ( wcsicmp ( name , L " width " ) = = 0 )
{
char * temp = hsWStringToString ( option ) ;
bookWidth = ( float ) atof ( temp ) ;
delete [ ] temp ;
}
}
fHeightScale = 1.f - bookHeight ;
fWidthScale = 1.f - bookWidth ;
// Still gotta create a new par chunk
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
case pfEsHTMLChunk : : kDecal :
c + = 6 ;
chunk = new pfEsHTMLChunk ( nil , 0 ) ;
chunk - > fType = pfEsHTMLChunk : : kDecal ;
while ( IGetNextOption ( c , name , option ) )
{
if ( wcsicmp ( name , L " align " ) = = 0 )
{
chunk - > fFlags & = ~ pfEsHTMLChunk : : kAlignMask ;
if ( wcsicmp ( option , L " left " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kLeft ;
else if ( wcsicmp ( option , L " center " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kCenter ;
else if ( wcsicmp ( option , L " right " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kRight ;
}
else if ( wcsicmp ( name , L " src " ) = = 0 )
{
// Name of mipmap source
chunk - > fImageKey = IGetMipmapKey ( option , hintLoc ) ;
}
else if ( wcsicmp ( name , L " pos " ) = = 0 )
{
chunk - > fFlags | = pfEsHTMLChunk : : kFloating ;
wchar_t * comma = wcschr ( option , L ' , ' ) ;
if ( comma ! = nil )
{
chunk - > fAbsoluteY = wcstol ( comma + 1 , NULL , 0 ) ;
* comma = 0 ;
}
chunk - > fAbsoluteX = wcstol ( option , NULL , 0 ) ;
}
else if ( wcsicmp ( name , L " resize " ) = = 0 )
{
if ( wcsicmp ( option , L " no " ) = = 0 )
chunk - > fNoResizeImg = true ;
}
else if ( wcsicmp ( name , L " tint " ) = = 0 )
{
if ( wcsicmp ( option , L " yes " ) = = 0 )
chunk - > fTintDecal = true ;
}
}
// add it to our cover decals list (this is tag is essentially thrown away as far as the parser cares)
if ( chunk - > fImageKey ! = nil )
fCoverDecals . Append ( chunk ) ;
else
delete chunk ;
// Start new paragraph chunk after this one
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
case pfEsHTMLChunk : : kMovie :
c + = 6 ;
chunk = new pfEsHTMLChunk ( nil , 0 ) ;
chunk - > fType = pfEsHTMLChunk : : kMovie ;
while ( IGetNextOption ( c , name , option ) )
{
if ( wcsicmp ( name , L " align " ) = = 0 )
{
chunk - > fFlags & = ~ pfEsHTMLChunk : : kAlignMask ;
if ( wcsicmp ( option , L " left " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kLeft ;
else if ( wcsicmp ( option , L " center " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kCenter ;
else if ( wcsicmp ( option , L " right " ) = = 0 )
chunk - > fFlags | = pfEsHTMLChunk : : kRight ;
}
else if ( wcsicmp ( name , L " src " ) = = 0 )
{
chunk - > fText = option ;
}
else if ( wcsicmp ( name , L " link " ) = = 0 )
{
chunk - > fEventID = wcstol ( option , NULL , 0 ) ;
chunk - > fFlags | = pfEsHTMLChunk : : kCanLink ;
}
else if ( wcsicmp ( name , L " pos " ) = = 0 )
{
chunk - > fFlags | = pfEsHTMLChunk : : kFloating ;
wchar_t * comma = wcschr ( option , L ' , ' ) ;
if ( comma ! = nil )
{
chunk - > fAbsoluteY = wcstol ( comma + 1 , NULL , 0 ) ;
* comma = 0 ;
}
chunk - > fAbsoluteX = wcstol ( option , NULL , 0 ) ;
}
else if ( wcsicmp ( name , L " resize " ) = = 0 )
{
if ( wcsicmp ( option , L " no " ) = = 0 )
chunk - > fNoResizeImg = true ;
}
else if ( wcsicmp ( name , L " oncover " ) = = 0 )
{
if ( wcsicmp ( option , L " yes " ) = = 0 )
chunk - > fOnCover = true ;
}
else if ( wcsicmp ( name , L " loop " ) = = 0 )
{
if ( wcsicmp ( option , L " no " ) = = 0 )
chunk - > fLoopMovie = false ;
}
}
chunk - > fMovieIndex = movieIndex ;
movieIndex + + ;
if ( chunk - > fOnCover )
{
if ( chunk - > fText ! = L " " )
fCoverDecals . Append ( chunk ) ;
else
delete chunk ;
}
else
{
if ( chunk - > fText ! = L " " )
fHTMLSource . Append ( chunk ) ;
else
delete chunk ;
}
// Start new paragraph chunk after this one
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
case pfEsHTMLChunk : : kEditable :
c + = 9 ;
SetEditable ( true ) ;
chunk = new pfEsHTMLChunk ( ) ;
while ( IGetNextOption ( c , name , option ) )
{
}
fHTMLSource . Append ( chunk ) ;
// Start new paragraph chunk after this one
lastParChunk = new pfEsHTMLChunk ( nil ) ;
lastParChunk - > fFlags = IFindLastAlignment ( ) ;
break ;
}
start = c ;
}
else
{
// Keep looking
c + + ;
}
}
// Final bit goes into the last paragraph chunk we had
if ( start = = c )
{
// No actual text, just delete
delete lastParChunk ;
lastParChunk = nil ;
}
else if ( lastParChunk ! = nil )
{
uint32_t count = ( uintptr_t ) c - ( uintptr_t ) start ;
wchar_t * temp = new wchar_t [ count + 1 ] ;
wcsncpy ( temp , start , count + 1 ) ;
lastParChunk - > fText = temp ;
delete [ ] temp ;
// Special case to remove any last trailing carriage return
// if( count > 1 && lastParChunk->fText[ count - 1 ] == '\n' )
// lastParChunk->fText[ count - 1 ] = 0;
fHTMLSource . Append ( lastParChunk ) ;
}
// Reset a few
fPageStarts . Reset ( ) ;
fPageStarts . Append ( 0 ) ;
if ( fAreEditing )
fLastPage = 0 ;
else
fLastPage = - 1 ;
return true ;
}
uint8_t pfJournalBook : : IGetTagType ( const wchar_t * string )
{
if ( string [ 0 ] ! = ' < ' )
return pfEsHTMLChunk : : kEmpty ;
struct TagRec
{
const wchar_t * fTag ;
uint8_t fType ;
} tags [ ] = { { L " p " , pfEsHTMLChunk : : kParagraph } ,
{ L " img " , pfEsHTMLChunk : : kImage } ,
{ L " pb " , pfEsHTMLChunk : : kPageBreak } ,
{ L " font " , pfEsHTMLChunk : : kFontChange } ,
{ L " margin " , pfEsHTMLChunk : : kMargin } ,
{ L " cover " , pfEsHTMLChunk : : kCover } ,
{ L " book " , pfEsHTMLChunk : : kBook } ,
{ L " decal " , pfEsHTMLChunk : : kDecal } ,
{ L " movie " , pfEsHTMLChunk : : kMovie } ,
{ L " editable " , pfEsHTMLChunk : : kEditable } ,
{ nil , pfEsHTMLChunk : : kEmpty } } ;
uint32_t i ;
for ( i = 0 ; tags [ i ] . fTag ! = nil ; i + + )
{
if ( wcsnicmp ( string + 1 , tags [ i ] . fTag , wcslen ( tags [ i ] . fTag ) ) = = 0 )
{
// Found tag--but only space or end tag marker allowed afterwards
char end = ( char ) string [ wcslen ( tags [ i ] . fTag ) + 1 ] ;
if ( end = = ' > ' | | end = = ' ' )
return tags [ i ] . fType ;
}
}
return pfEsHTMLChunk : : kEmpty ;
}
bool pfJournalBook : : IGetNextOption ( const wchar_t * & string , wchar_t * name , wchar_t * option )
{
const wchar_t * c ;
// Advance past any white space
while ( * string = = L ' ' )
string + + ;
if ( * string = = L ' > ' )
{
string + + ;
return false ;
}
// Advance to =
c = string ;
while ( * string ! = L ' > ' & & * string ! = L ' ' & & * string ! = L ' = ' & & * string ! = L ' \0 ' )
string + + ;
if ( * string ! = L ' = ' )
return false ;
// Copy name
uint32_t len = ( ( uintptr_t ) string - ( uintptr_t ) c ) / 2 ; // divide length by 2 because each character is two bytes
wcsncpy ( name , c , len ) ;
name [ len ] = L ' \0 ' ;
// Find start of option value
string + + ;
while ( * string = = L ' ' )
string + + ;
if ( * string = = L ' \0 ' | | * string = = L ' > ' )
return false ;
if ( * string = = L ' \" ' )
{
// Search for other quote
string + + ;
c = string ;
while ( * string ! = L ' > ' & & * string ! = L ' \" ' & & * string ! = L ' \0 ' )
string + + ;
len = ( ( uintptr_t ) string - ( uintptr_t ) c ) / 2 ; // divide length by 2 because each character is two bytes
wcsncpy ( option , c , len ) ;
option [ len ] = L ' \0 ' ;
if ( * string = = L ' \" ' )
string + + ;
return true ;
}
// Non-quoted token
c = string ;
while ( * string ! = L ' ' & & * string ! = L ' > ' & & * string ! = L ' \0 ' )
string + + ;
len = ( ( uintptr_t ) string - ( uintptr_t ) c ) / 2 ; // divide length by 2 because each character is two bytes
wcsncpy ( option , c , len ) ;
option [ len ] = L ' \0 ' ;
return true ;
}
void pfJournalBook : : IFreeSource ( void )
{
uint32_t i ;
for ( i = 0 ; i < fHTMLSource . GetCount ( ) ; i + + )
delete fHTMLSource [ i ] ;
fHTMLSource . Reset ( ) ;
for ( i = 0 ; i < fCoverDecals . GetCount ( ) ; i + + )
delete fCoverDecals [ i ] ;
fCoverDecals . Reset ( ) ;
for ( i = 0 ; i < fLoadedMovies . GetCount ( ) ; i + + )
{
plLayerAVI * movie = fLoadedMovies [ i ] - > movieLayer ;
movie - > GetKey ( ) - > UnRefObject ( ) ;
delete fLoadedMovies [ i ] ;
}
fLoadedMovies . Reset ( ) ;
}
//// IGetMipmapKey ///////////////////////////////////////////////////////////
// Looks up the key for a mipmap given the image name. Note that the given
// location is treated as a hint; if the image isn't found in that location,
// the code will attempt to look in the currently loaded age for a matching
// image name.
# ifndef PLASMA_EXTERNAL_RELEASE
# include "plGImage/plJPEG.h"
# include "plGImage/plPNG.h"
# endif
plKey pfJournalBook : : IGetMipmapKey ( const wchar_t * name , const plLocation & loc )
{
plString cName = plString : : FromWchar ( name ) ;
# ifndef PLASMA_EXTERNAL_RELEASE
if ( cName . Find ( ' / ' ) > = 0 | | cName . Find ( ' \\ ' ) > = 0 )
{
// For internal use only--allow local path names of PNG and JPEG images, to
// facilitate fast prototyping
plMipmap * mip ;
if ( cName . Find ( " .png " ) > = 0 )
mip = plPNG : : Instance ( ) . ReadFromFile ( cName . c_str ( ) ) ;
else
mip = plJPEG : : Instance ( ) . ReadFromFile ( cName . c_str ( ) ) ;
hsgResMgr : : ResMgr ( ) - > NewKey ( cName , mip , loc ) ;
return mip - > GetKey ( ) ;
}
# endif
// Try first to find in the given location
plUoid myUoid ( loc , plMipmap : : Index ( ) , cName ) ;
plKey key = hsgResMgr : : ResMgr ( ) - > FindKey ( myUoid ) ;
if ( key ! = nil )
{
return key ;
}
// Next, try our "global" pre-defined age
const plLocation & globLoc = plKeyFinder : : Instance ( ) . FindLocation ( " GUI " , " BkBookImages " ) ;
myUoid = plUoid ( globLoc , plMipmap : : Index ( ) , cName ) ;
key = hsgResMgr : : ResMgr ( ) - > FindKey ( myUoid ) ;
if ( key ! = nil )
{
return key ;
}
// Do a search through our current age with just the name given
if ( plNetClientMgr : : GetInstance ( ) ! = nil )
{
plString thisAge = plAgeLoader : : GetInstance ( ) - > GetCurrAgeDesc ( ) . GetAgeName ( ) ;
if ( ! thisAge . IsNull ( ) )
{
key = plKeyFinder : : Instance ( ) . StupidSearch ( thisAge , " " , plMipmap : : Index ( ) , cName , true ) ;
if ( key ! = nil )
{
return key ;
}
}
}
return nil ;
}
//// IRenderPage /////////////////////////////////////////////////////////////
// Takes the given page out of the source and renders it into the specified
// DTMap (by GUI tag ID). If no page is cached after this one, also updates
// the various cached info about page endings, etc.
void pfJournalBook : : IRenderPage ( uint32_t page , uint32_t whichDTMap , bool suppressRendering /*= false*/ )
{
if ( fAreEditing )
return ; // we don't render if we are editing the book
// Grab the DTMap via the GUI system
plDynamicTextMap * dtMap = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( whichDTMap ) ;
hsAssert ( dtMap ! = nil , " Invalid DT map in IRenderPage() " ) ;
loadedMovie * movie = nil ;
bool movieAlreadyLoaded = false ;
// Make sure our page starts are up-to-snuff, at least to this point
IRecalcPageStarts ( page ) ;
// Render!
hsColorRGBA color ;
color . Set ( 0 , 0 , 0 , 0 ) ;
if ( ! suppressRendering )
dtMap - > ClearToColor ( color ) ;
hsGMaterial * material = nil ;
if ( whichDTMap = = pfJournalDlgProc : : kTagLeftDTMap )
material = fBookGUIs [ fCurBookGUI ] - > PageMaterial ( pfBookData : : kLeftPage ) ;
else if ( whichDTMap = = pfJournalDlgProc : : kTagRightDTMap )
material = fBookGUIs [ fCurBookGUI ] - > PageMaterial ( pfBookData : : kRightPage ) ;
else if ( whichDTMap = = pfJournalDlgProc : : kTagTurnFrontDTMap )
material = fBookGUIs [ fCurBookGUI ] - > PageMaterial ( pfBookData : : kTurnFrontPage ) ;
else if ( whichDTMap = = pfJournalDlgProc : : kTagTurnBackDTMap )
material = fBookGUIs [ fCurBookGUI ] - > PageMaterial ( pfBookData : : kTurnBackPage ) ;
if ( material )
{
// clear any exiting layers (movies) from the material
int i ;
for ( i = 0 ; i < material - > GetNumLayers ( ) ; i + + ) // remove all plLayerMovie layers
{
plLayerInterface * matLayer = material - > GetLayer ( i ) ;
plLayerAVI * movie = plLayerAVI : : ConvertNoRef ( matLayer ) ;
if ( movie ) // if it was a movie layer
{
plMatRefMsg * refMsg = new plMatRefMsg ( material - > GetKey ( ) , plRefMsg : : kOnRemove , i , plMatRefMsg : : kLayer ) ; // remove it
hsgResMgr : : ResMgr ( ) - > SendRef ( material - > GetLayer ( i ) - > GetKey ( ) , refMsg , plRefFlags : : kActiveRef ) ;
}
}
}
hsAssert ( page < fPageStarts . GetCount ( ) | | page > fLastPage , " UnInitialized page start! " ) ;
if ( page < = fLastPage
& & page < fPageStarts . GetCount ( ) ) // Added this as a crash-prevention bandaid - MT
{
uint32_t idx ;
uint16_t width , height , y , x , ascent , lastX , lastY ;
uint8_t fontFlags , fontSize ;
plString fontFace ;
hsColorRGBA fontColor ;
int16_t fontSpacing ;
bool needSFX = false ;
// Find and set initial font properties
IFindFontProps ( fPageStarts [ page ] , fontFace , fontSize , fontFlags , fontColor , fontSpacing ) ;
dtMap - > SetFont ( fontFace , fontSize , fontFlags , false ) ;
dtMap - > SetTextColor ( fontColor , true ) ;
dtMap - > SetLineSpacing ( fontSpacing ) ;
for ( idx = fPageStarts [ page ] , x = ( uint16_t ) fPageLMargin , y = ( uint16_t ) fPageTMargin ;
y < ( uint16_t ) ( 512 - fPageTMargin - fPageBMargin ) & & idx < fHTMLSource . GetCount ( ) ; idx + + )
{
if ( fPageStarts . GetCount ( ) > page + 1 & & idx = = fPageStarts [ page + 1 ] )
break ; // Just go ahead and break at the start of the next page, since we already found it
pfEsHTMLChunk * chunk = fHTMLSource [ idx ] ;
switch ( chunk - > fType )
{
case pfEsHTMLChunk : : kParagraph :
if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kLeft )
{
dtMap - > SetJustify ( plDynamicTextMap : : kLeftJustify ) ;
x = ( uint16_t ) fPageLMargin ; // reset X if our justification changes
}
else if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kRight )
{
dtMap - > SetJustify ( plDynamicTextMap : : kRightJustify ) ;
x = ( uint16_t ) fPageLMargin ; // reset X if our justification changes
}
else if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kCenter )
{
dtMap - > SetJustify ( plDynamicTextMap : : kCenter ) ;
x = ( uint16_t ) fPageLMargin ; // reset X if our justification changes
}
dtMap - > SetFirstLineIndent ( ( int16_t ) ( x - fPageLMargin ) ) ;
width = ( uint16_t ) ( 512 - fPageLMargin - fPageRMargin ) ;
height = ( uint16_t ) ( 512 - fPageBMargin - y ) ;
uint32_t lastChar ;
dtMap - > CalcWrappedStringSize ( chunk - > fText . c_str ( ) , & width , & height , & lastChar , & ascent , & lastX , & lastY ) ;
width = ( uint16_t ) ( 512 - fPageLMargin - fPageRMargin ) ;
if ( ! suppressRendering )
dtMap - > DrawWrappedString ( ( uint16_t ) fPageLMargin , y , chunk - > fText . c_str ( ) , width , ( uint16_t ) ( 512 - fPageBMargin - y ) , & lastX , & lastY ) ;
if ( lastChar = = 0 )
{
// This paragraph didn't fit on this page at *all*, so just bump it to the next
// one artificially (the -- is to account for the for loop; see image handling below)
y + = 512 ;
if ( idx > fPageStarts [ page ] )
idx - - ;
break ;
}
if ( lastChar < chunk - > fText . size ( ) & & chunk - > fText [ lastChar ] ! = 0 )
{
// Didn't get to render the whole paragraph in this go, so we're going to cheat
// and split the paragraph up into two so that we can handle it properly. Note:
// this changes the chunk array beyond this point, so we need to invalidate the
// cache, but that's ok 'cause if we're doing this, it's probably invalid (or empty)
// anyway
int fTextLen = chunk - > fText . length ( ) ;
wchar_t * s = new wchar_t [ fTextLen + 1 ] ;
wcscpy ( s , chunk - > fText . c_str ( ) ) ;
s [ fTextLen ] = L ' \0 ' ;
// Note: Makes a copy of the string
pfEsHTMLChunk * c2 = new pfEsHTMLChunk ( & s [ lastChar ] ) ;
c2 - > fFlags = chunk - > fFlags ;
fHTMLSource . Insert ( idx + 1 , c2 ) ;
// Clip and reallocate so we don't have two copies laying around
s [ lastChar ] = L ' \0 ' ;
chunk - > fText = s ;
delete [ ] s ;
// Invalidate our cache starting with the next page
if ( fPageStarts . GetCount ( ) > page + 1 )
fPageStarts . SetCount ( page + 1 ) ;
y + = 512 ;
break ;
}
x = lastX ;
y = ( uint16_t ) ( lastY - dtMap - > GetCurrFont ( ) - > GetAscent ( ) ) ; // Since our text is top-justified
if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) ! = pfEsHTMLChunk : : kLeft )
{
// Ending X is not guaranteed to be anything useful if we're not left justified
x = ( uint16_t ) fPageLMargin ;
}
break ;
case pfEsHTMLChunk : : kImage :
{
plMipmap * mip = plMipmap : : ConvertNoRef ( chunk - > fImageKey ! = nil ? chunk - > fImageKey - > ObjectIsLoaded ( ) : nil ) ;
if ( mip ! = nil )
{
// First, determine if we need to be processing FX messages
if ( chunk - > fFlags & pfEsHTMLChunk : : kGlowing )
needSFX = true ;
else if ( chunk - > fFlags & pfEsHTMLChunk : : kActAsCB )
{
// If our color doesn't match our checked state, we want to be fading it in
hsColorRGBA & want = ( chunk - > fFlags & pfEsHTMLChunk : : kChecked ) ? chunk - > fOnColor : chunk - > fOffColor ;
if ( want ! = chunk - > fCurrColor )
needSFX = true ;
}
if ( chunk - > fFlags & pfEsHTMLChunk : : kFloating )
{
// Floating image, ignore the text flow completely and just splat the image on!
IDrawMipmap ( chunk , chunk - > fAbsoluteX , chunk - > fAbsoluteY , mip , dtMap , whichDTMap , suppressRendering ) ;
}
else
{
if ( y + mip - > GetHeight ( ) > = 512 - fPageBMargin )
{
// Mipmap overlaps the bottom of this page, so forcibly break so we'll
// end up marking the page break here (note that, unlike paragraphs, we
// can't really break the mipmap into two...well, OK, we could, but it
// wouldn't make much sense :)
y + = ( uint16_t ) ( mip - > GetHeight ( ) ) ;
// Wonderful, the break breaks us from the switch(), which means the for()
// loops runs once more and increments idx. So this is to counter that.
// (We better check tho, just to make sure nobody feeds us an extra-large
// image and sends us on an infinite loop)
if ( idx > fPageStarts [ page ] )
idx - - ;
break ;
}
if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kLeft )
x = ( uint16_t ) fPageLMargin ;
else if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kRight )
x = ( uint16_t ) ( 512 - fPageRMargin - mip - > GetWidth ( ) ) ;
else if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kCenter )
x = ( uint16_t ) ( 256 - ( mip - > GetWidth ( ) > > 1 ) ) ;
IDrawMipmap ( chunk , x , y , mip , dtMap , whichDTMap , suppressRendering ) ;
y + = ( uint16_t ) ( mip - > GetHeight ( ) ) ;
x = ( uint16_t ) fPageLMargin ;
}
}
}
break ;
case pfEsHTMLChunk : : kPageBreak :
// Time for some creative guesswork. See, if the user put a <pb> in at the end of a page,
// but coincidentally we already broke to a new page, then the explicit <pb> is redundant,
// so we don't want it. However, if we're at a new page *because* of a <pb>, then a new <pb>
// is intentional, so we DO want to process it.
if ( idx = = fPageStarts [ page ] )
{
// Did we get here b/c the last chunk was a pb?
if ( idx > 0 & & fHTMLSource [ idx - 1 ] - > fType ! = pfEsHTMLChunk : : kPageBreak )
{
// Nope, so we DO want to ignore it!
continue ;
}
}
y = ( uint16_t ) ( 512 - fPageTMargin - fPageBMargin ) ;
x = ( uint16_t ) fPageLMargin ;
break ;
case pfEsHTMLChunk : : kFontChange :
IFindFontProps ( idx , fontFace , fontSize , fontFlags , fontColor , fontSpacing ) ;
dtMap - > SetFont ( fontFace , fontSize , fontFlags , false ) ;
dtMap - > SetTextColor ( fontColor , true ) ;
dtMap - > SetLineSpacing ( fontSpacing ) ;
break ;
case pfEsHTMLChunk : : kMovie :
movieAlreadyLoaded = ( IMovieAlreadyLoaded ( chunk ) ! = nil ) ; // have we already cached it?
plLayerAVI * movieLayer = IMakeMovieLayer ( chunk , x , y , ( plMipmap * ) dtMap , whichDTMap , suppressRendering ) ;
if ( movieLayer )
{
// adjust the starting height of the movie if we are keeping it inline with the text
uint32_t movieHeight = 0 , movieWidth = 0 ;
movieHeight = movieLayer - > GetHeight ( ) ;
movieWidth = movieLayer - > GetWidth ( ) ;
if ( ! ( chunk - > fFlags & pfEsHTMLChunk : : kFloating ) )
{
if ( y + movieHeight > = 512 - fPageBMargin )
{
// Movie overlaps the bottom of this page, so forcibly break so we'll
// end up marking the page break here (note that, unlike paragraphs, we
// can't really break the Movie into two)
y + = ( uint16_t ) movieHeight ;
// Wonderful, the break breaks us from the switch(), which means the for()
// loops runs once more and increments idx. So this is to counter that.
// (We better check tho, just to make sure nobody feeds us an extra-large
// image and sends us on an infinite loop)
if ( idx > fPageStarts [ page ] )
idx - - ;
break ;
}
y + = ( uint16_t ) movieHeight ;
x = ( uint16_t ) fPageLMargin ;
}
if ( ! movieAlreadyLoaded ) // if the movie wasn't already cached, cache it
{
movie = new loadedMovie ;
movie - > movieLayer = movieLayer ; // save the layer and chunk data
movie - > movieChunk = chunk ;
fLoadedMovies . Append ( movie ) ;
movie = nil ;
movieAlreadyLoaded = false ;
}
if ( material & & ! suppressRendering )
material - > AddLayerViaNotify ( movieLayer ) ;
}
break ;
}
}
if ( fPageStarts . GetCount ( ) < = page + 1 )
fPageStarts . ExpandAndZero ( page + 2 ) ;
fPageStarts [ page + 1 ] = idx ;
if ( idx = = fHTMLSource . GetCount ( ) )
fLastPage = page ;
pfBookData : : WhichSide thisWhich = ( whichDTMap = = pfJournalDlgProc : : kTagRightDTMap ) ? pfBookData : : kRightSide : ( whichDTMap = = pfJournalDlgProc : : kTagLeftDTMap ) ? pfBookData : : kLeftSide : pfBookData : : kNoSides ;
if ( needSFX )
fBookGUIs [ fCurBookGUI ] - > RegisterForSFX ( ( pfBookData : : WhichSide ) ( fBookGUIs [ fCurBookGUI ] - > CurSFXPages ( ) | thisWhich ) ) ;
else
fBookGUIs [ fCurBookGUI ] - > RegisterForSFX ( ( pfBookData : : WhichSide ) ( fBookGUIs [ fCurBookGUI ] - > CurSFXPages ( ) & ~ thisWhich ) ) ;
}
if ( ! suppressRendering )
dtMap - > FlushToHost ( ) ;
}
//// IMoveMovies /////////////////////////////////////////////////////////////
void pfJournalBook : : IMoveMovies ( hsGMaterial * source , hsGMaterial * dest )
{
hsTArray < plLayerAVI * > moviesOnPage ;
if ( source & & dest )
{
// clear any exiting layers (movies) from the material and save them to our local array
int i ;
for ( i = 0 ; i < source - > GetNumLayers ( ) ; i + + ) // remove all plLayerMovie layers
{
plLayerInterface * matLayer = source - > GetLayer ( i ) ;
plLayerAVI * movie = plLayerAVI : : ConvertNoRef ( matLayer ) ;
if ( movie ) // if it was a movie layer
{
plMatRefMsg * refMsg = new plMatRefMsg ( source - > GetKey ( ) , plRefMsg : : kOnRemove , i , plMatRefMsg : : kLayer ) ; // remove it
hsgResMgr : : ResMgr ( ) - > SendRef ( source - > GetLayer ( i ) - > GetKey ( ) , refMsg , plRefFlags : : kActiveRef ) ;
moviesOnPage . Append ( movie ) ;
}
}
// clear the destination's movies (if it has any)
for ( i = 0 ; i < dest - > GetNumLayers ( ) ; i + + ) // remove all plLayerMovie layers
{
plLayerInterface * matLayer = dest - > GetLayer ( i ) ;
plLayerAVI * movie = plLayerAVI : : ConvertNoRef ( matLayer ) ;
if ( movie ) // if it was a movie layer
{
plMatRefMsg * refMsg = new plMatRefMsg ( dest - > GetKey ( ) , plRefMsg : : kOnRemove , i , plMatRefMsg : : kLayer ) ; // remove it
hsgResMgr : : ResMgr ( ) - > SendRef ( dest - > GetLayer ( i ) - > GetKey ( ) , refMsg , plRefFlags : : kActiveRef ) ;
}
}
// put the movies we ripped off the old page onto the new one
for ( i = 0 ; i < moviesOnPage . GetCount ( ) ; i + + )
{
dest - > AddLayerViaNotify ( moviesOnPage [ i ] ) ;
}
}
}
//// IDrawMipmap /////////////////////////////////////////////////////////////
void pfJournalBook : : IDrawMipmap ( pfEsHTMLChunk * chunk , uint16_t x , uint16_t y , plMipmap * mip , plDynamicTextMap * dtMap , uint32_t whichDTMap , bool dontRender )
{
plMipmap * copy = new plMipmap ( ) ;
copy - > CopyFrom ( mip ) ;
if ( chunk - > fNoResizeImg )
{
// book is NOT square, there is a h/w ratio of 1/0.7
// calc new size based on how the book has been skewed
float xScale = ( fWidthScale = = 0 ) ? 1 : 1 / ( 1 - fWidthScale ) ;
float yScale = ( fHeightScale = = 0 ) ? 1 : 1 / ( 1 - fHeightScale ) ;
yScale * = 0.7 ; // adjust because the book isn't square
uint32_t width = ( uint32_t ) ( mip - > GetWidth ( ) * xScale ) ;
uint32_t height = ( uint32_t ) ( mip - > GetHeight ( ) * yScale ) ;
uint16_t xShift ;
uint16_t yShift ;
if ( dtMap - > GetWidth ( ) < width ) width = dtMap - > GetWidth ( ) ;
if ( dtMap - > GetHeight ( ) < height ) height = dtMap - > GetHeight ( ) ;
if ( height < mip - > GetHeight ( ) )
{
yShift = ( uint16_t ) ( ( mip - > GetHeight ( ) - height ) / 2 ) ;
if ( y + yShift + height > dtMap - > GetHeight ( ) )
y = ( uint16_t ) ( dtMap - > GetHeight ( ) - height ) ;
else
y + = yShift ;
}
else
{
yShift = ( uint16_t ) ( ( height - mip - > GetHeight ( ) ) / 2 ) ;
if ( yShift > y )
y = 0 ;
else
y - = yShift ;
}
if ( width < mip - > GetWidth ( ) )
{
xShift = ( uint16_t ) ( ( mip - > GetWidth ( ) - width ) / 2 ) ;
if ( x + xShift + width > dtMap - > GetWidth ( ) )
x = ( uint16_t ) ( dtMap - > GetWidth ( ) - width ) ;
else
x + = xShift ;
}
else
{
xShift = ( uint16_t ) ( ( width - mip - > GetWidth ( ) ) / 2 ) ;
if ( xShift > x )
x = 0 ;
else
x - = xShift ;
}
copy - > SetCurrLevel ( 0 ) ; // resize the image so it will look unchanged when rendered on the altered book
copy - > ResizeNicely ( ( uint16_t ) width , ( uint16_t ) height , plMipmap : : kDefaultFilter ) ;
}
if ( ! dontRender )
{
plMipmap : : CompositeOptions opts ;
if ( chunk - > fFlags & pfEsHTMLChunk : : kActAsCB )
{
opts . fFlags = ( chunk - > fFlags & pfEsHTMLChunk : : kBlendAlpha ) ? 0 : plMipmap : : kBlendWriteAlpha ;
opts . fRedTint = chunk - > fCurrColor . r ;
opts . fGreenTint = chunk - > fCurrColor . g ;
opts . fBlueTint = chunk - > fCurrColor . b ;
opts . fOpacity = ( uint8_t ) ( chunk - > fCurrColor . a * 255.f ) ;
}
else
{
if ( chunk - > fFlags & pfEsHTMLChunk : : kGlowing )
opts . fFlags = ( chunk - > fFlags & pfEsHTMLChunk : : kBlendAlpha ) ? 0 : plMipmap : : kMaskSrcAlpha ;
else if ( chunk - > fFlags & pfEsHTMLChunk : : kTranslucent )
opts . fFlags = plMipmap : : kMaskSrcAlpha ;
else
opts . fFlags = ( chunk - > fFlags & pfEsHTMLChunk : : kBlendAlpha ) ? plMipmap : : kCopySrcAlpha : plMipmap : : kForceOpaque ;
opts . fOpacity = ( uint8_t ) ( chunk - > fCurrOpacity * 255.f ) ;
}
dtMap - > Composite ( copy , x , y , & opts ) ;
}
if ( chunk - > fFlags & pfEsHTMLChunk : : kCanLink )
{
if ( whichDTMap = = pfJournalDlgProc : : kTagRightDTMap | | whichDTMap = = pfJournalDlgProc : : kTagTurnFrontDTMap )
x + = ( uint16_t ) ( dtMap - > GetWidth ( ) ) ; // Right page rects are offsetted to differentiate
if ( dontRender ) // if we aren't rendering then this link isn't visible, but the index still needs to be valid, so give it a rect of 0,0,0,0
chunk - > fLinkRect . Set ( 0 , 0 , 0 , 0 ) ;
else
chunk - > fLinkRect . Set ( x , y , ( int16_t ) ( copy - > GetWidth ( ) ) , ( int16_t ) ( copy - > GetHeight ( ) ) ) ;
fVisibleLinks . Append ( chunk ) ;
}
delete copy ;
}
pfJournalBook : : loadedMovie * pfJournalBook : : IMovieAlreadyLoaded ( pfEsHTMLChunk * chunk )
{
int i ;
for ( i = 0 ; i < fLoadedMovies . GetCount ( ) ; i + + ) // filename and id# must both match
{
if ( ( chunk - > fText = = fLoadedMovies [ i ] - > movieChunk - > fText ) & & ( chunk - > fMovieIndex = = fLoadedMovies [ i ] - > movieChunk - > fMovieIndex ) )
return fLoadedMovies [ i ] ;
}
return nil ;
}
plKey pfJournalBook : : GetMovie ( uint8_t index )
{
loadedMovie * movie = IGetMovieByIndex ( index ) ;
if ( movie )
return movie - > movieLayer - > GetKey ( ) ;
return plKey ( nil ) ;
}
pfJournalBook : : loadedMovie * pfJournalBook : : IGetMovieByIndex ( uint8_t index )
{
int i ;
for ( i = 0 ; i < fLoadedMovies . GetCount ( ) ; i + + )
{
if ( fLoadedMovies [ i ] - > movieChunk - > fMovieIndex = = index )
return fLoadedMovies [ i ] ;
}
return nil ;
}
plLayerAVI * pfJournalBook : : IMakeMovieLayer ( pfEsHTMLChunk * chunk , uint16_t x , uint16_t y , plMipmap * baseMipmap , uint32_t whichDTMap , bool dontRender )
{
// see if it's already loaded
loadedMovie * movie = IMovieAlreadyLoaded ( chunk ) ;
plLayer * layer = nil ;
plLayerAVI * movieLayer = nil ;
uint16_t movieWidth = 0 , movieHeight = 0 ;
if ( movie )
{
movieLayer = movie - > movieLayer ;
layer = plLayer : : ConvertNoRef ( movieLayer - > BottomOfStack ( ) ) ;
movieWidth = movieLayer - > GetWidth ( ) ;
movieHeight = movieLayer - > GetHeight ( ) ;
}
else
{
// Create the layer and register it.
// We'll need a unique name. This is a hack, but an effective hack.
static int uniqueSuffix = 0 ;
plString buff ;
buff = plFormat ( " {}_{}_ml " , GetKey ( ) - > GetName ( ) , uniqueSuffix ) ;
layer = new plLayer ;
hsgResMgr : : ResMgr ( ) - > NewKey ( buff , layer , GetKey ( ) - > GetUoid ( ) . GetLocation ( ) ) ;
buff = plFormat ( " {}_{}_m " , GetKey ( ) - > GetName ( ) , uniqueSuffix + + ) ;
movieLayer = new plLayerAVI ;
hsgResMgr : : ResMgr ( ) - > NewKey ( buff , movieLayer , GetKey ( ) - > GetUoid ( ) . GetLocation ( ) ) ;
movieLayer - > GetKey ( ) - > RefObject ( ) ; // we want to own a ref so we can nuke it at will
movieLayer - > AttachViaNotify ( layer ) ;
// Initialize it.
char * name = hsWStringToString ( chunk - > fText . c_str ( ) ) ;
movieLayer - > SetMovieName ( name ) ;
delete [ ] name ;
movieLayer - > Eval ( 0 , 0 , 0 ) ; // set up the movie
movieWidth = movieLayer - > GetWidth ( ) ;
movieHeight = movieLayer - > GetHeight ( ) ;
if ( movieHeight = = 0 | | movieWidth = = 0 ) // problem loading the file
{
movieLayer - > GetKey ( ) - > UnRefObject ( ) ;
return nil ;
}
}
if ( layer )
{
layer - > InitToDefault ( ) ;
layer - > SetAmbientColor ( hsColorRGBA ( ) . Set ( 0.f , 0.f , 0.f , 1.0f ) ) ;
layer - > SetOpacity ( 1.0f ) ;
// Blend flags, movies are opaque, but they don't take the whole page, so alphamapping them
layer - > SetBlendFlags ( hsGMatState : : kBlendAlpha ) ;
// Movie shouldn't have to ZWrite
layer - > SetZFlags ( hsGMatState : : kZNoZWrite ) ;
// No special shading.
layer - > SetShadeFlags ( 0 ) ;
// Clamp all textures.
layer - > SetClampFlags ( hsGMatState : : kClampTexture ) ;
// Draw passes individually.
layer - > SetMiscFlags ( hsGMatState : : kMiscRestartPassHere ) ;
// Shared UV coordinates.
layer - > SetUVWSrc ( 0 ) ;
if ( chunk - > fFlags & pfEsHTMLChunk : : kFloating )
{
// Floating movie, ignore the text flow completely and just splat the movie on!
x = chunk - > fAbsoluteX ;
y = chunk - > fAbsoluteY ;
}
if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kLeft )
x = ( uint16_t ) fPageLMargin ;
else if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kRight )
x = ( uint16_t ) ( baseMipmap - > GetWidth ( ) - fPageRMargin - movieWidth ) ;
else if ( ( chunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kCenter )
x = ( uint16_t ) ( ( baseMipmap - > GetWidth ( ) > > 1 ) - ( movieWidth > > 1 ) ) ;
// x and y are in pixels, need to convert to a range of 0 to 1
float xRel = ( float ) x / ( float ) baseMipmap - > GetWidth ( ) ;
float yRel = ( float ) y / ( float ) baseMipmap - > GetHeight ( ) ;
// Need to convert the scaling to texture space
float xScale = ( float ) baseMipmap - > GetWidth ( ) / ( float ) movieWidth ;
float yScale = ( float ) baseMipmap - > GetHeight ( ) / ( float ) movieHeight ;
if ( chunk - > fNoResizeImg )
{
// book is NOT square, there is a h/w ratio of 1/0.7
// calc new size based on how the book has been skewed
xScale * = ( fWidthScale = = 0 ) ? 1 : ( 1 - fWidthScale ) ;
yScale * = ( fHeightScale = = 0 ) ? 1 : ( 1 - fHeightScale ) ;
yScale * = 1 / 0.7 ; // adjust because the book isn't square
}
hsVector3 scaleVec ( xScale , yScale , 1 ) , translateVec ( - ( xRel * xScale ) , - ( yRel * yScale ) , 0 ) ;
hsMatrix44 scaleMat , translateMat ;
scaleMat . MakeScaleMat ( & scaleVec ) ;
translateMat . MakeTranslateMat ( & translateVec ) ;
hsMatrix44 flipMat ;
if ( chunk - > fOnCover ) // cover movies need to be y flipped
{
hsVector3 yTransVec ( 0 , - 1 , 0 ) , invertYVec ( 1 , - 1 , 1 ) ;
hsMatrix44 invertY , transY ;
invertY . MakeScaleMat ( & invertYVec ) ;
transY . MakeTranslateMat ( & yTransVec ) ;
flipMat = invertY * transY ;
}
else // left page movies need to be x flipped
{
if ( ( whichDTMap = = pfJournalDlgProc : : kTagLeftDTMap ) | | ( whichDTMap = = pfJournalDlgProc : : kTagTurnBackDTMap ) )
{
hsVector3 xTransVec ( - 1 , 0 , 0 ) , invertXVec ( - 1 , 1 , 1 ) ;
hsMatrix44 invertX , transX ;
invertX . MakeScaleMat ( & invertXVec ) ;
transX . MakeTranslateMat ( & xTransVec ) ;
flipMat = invertX * transX ;
}
else
flipMat = hsMatrix44 : : IdentityMatrix ( ) ;
}
hsMatrix44 xfm ;
xfm = translateMat * scaleMat * flipMat ;
layer - > SetTransform ( xfm ) ;
}
if ( chunk - > fFlags & pfEsHTMLChunk : : kCanLink )
{
if ( whichDTMap = = pfJournalDlgProc : : kTagRightDTMap | | whichDTMap = = pfJournalDlgProc : : kTagTurnFrontDTMap )
x + = ( uint16_t ) ( baseMipmap - > GetWidth ( ) ) ; // Right page rects are offsetted to differentiate
if ( dontRender ) // if we aren't rendering then this link isn't visible, but the index still needs to be valid, so give it a rect of 0,0,0,0
chunk - > fLinkRect . Set ( 0 , 0 , 0 , 0 ) ;
else
chunk - > fLinkRect . Set ( x , y , movieWidth , movieHeight ) ;
fVisibleLinks . Append ( chunk ) ;
}
plAnimTimeConvert & timeConvert = movieLayer - > GetTimeConvert ( ) ;
timeConvert . SetBegin ( 0 ) ;
timeConvert . SetEnd ( movieLayer - > GetLength ( ) ) ;
if ( chunk - > fLoopMovie )
{
timeConvert . SetLoopPoints ( 0 , movieLayer - > GetLength ( ) ) ;
timeConvert . Loop ( ) ;
}
timeConvert . Start ( ) ; // start the show!
return movieLayer ;
}
plLayerInterface * pfJournalBook : : IMakeBaseLayer ( plMipmap * image )
{
// Create the layer and register it.
// We'll need a unique name. This is a hack, but an effective hack.
static int uniqueSuffix = 0 ;
plString buff = plFormat ( " {}_{} " , GetKey ( ) - > GetName ( ) , uniqueSuffix + + ) ;
plLayer * layer = new plLayer ;
hsgResMgr : : ResMgr ( ) - > NewKey ( buff , layer , GetKey ( ) - > GetUoid ( ) . GetLocation ( ) ) ;
// Initialize it.
layer - > InitToDefault ( ) ;
layer - > SetAmbientColor ( hsColorRGBA ( ) . Set ( 0.f , 0.f , 0.f , 1.f ) ) ;
if ( fTintCover )
layer - > SetRuntimeColor ( hsColorRGBA ( ) . Set ( fCoverTint . r , fCoverTint . g , fCoverTint . b , 1.f ) ) ;
layer - > SetOpacity ( 1.f ) ;
// Blend flags, opaque for the bottom layer.
layer - > SetBlendFlags ( 0 ) ;
// Only the bottom layer writes it's Z value.
layer - > SetZFlags ( 0 ) ;
// No special shading.
layer - > SetShadeFlags ( hsGMatState : : kShadeReallyNoFog ) ;
// Clamp all textures.
layer - > SetClampFlags ( hsGMatState : : kClampTexture ) ;
// Draw passes individually.
layer - > SetMiscFlags ( hsGMatState : : kMiscRestartPassHere ) ;
// Shared UV coordinates.
layer - > SetUVWSrc ( 0 ) ;
// Set up the transform.
hsVector3 yTransVec ( 0 , - 1 , 0 ) , invertYVec ( 1 , - 1 , 1 ) ;
hsMatrix44 xfm , invertY , transY , flipY ;
invertY . MakeScaleMat ( & invertYVec ) ;
transY . MakeTranslateMat ( & yTransVec ) ;
flipY = invertY * transY ;
xfm = flipY ;
layer - > SetTransform ( xfm ) ;
// Set the texture (assumes mipmap is non-nil).
plLayRefMsg * refMsg = new plLayRefMsg ( layer - > GetKey ( ) , plRefMsg : : kOnCreate , 0 , plLayRefMsg : : kTexture ) ;
hsgResMgr : : ResMgr ( ) - > SendRef ( image - > GetKey ( ) , refMsg , plRefFlags : : kActiveRef ) ;
return plLayerInterface : : ConvertNoRef ( layer ) ;
}
plLayerInterface * pfJournalBook : : IMakeDecalLayer ( pfEsHTMLChunk * decalChunk , plMipmap * decal , plMipmap * baseMipmap )
{
// Create the layer and register it.
// We'll need a unique name. This is a hack, but an effective hack.
static int uniqueSuffix = 0 ;
plString buff = plFormat ( " {}_{}_d " , GetKey ( ) - > GetName ( ) , uniqueSuffix + + ) ;
plLayer * layer = new plLayer ;
hsgResMgr : : ResMgr ( ) - > NewKey ( buff , layer , GetKey ( ) - > GetUoid ( ) . GetLocation ( ) ) ;
// Initialize it.
layer - > InitToDefault ( ) ;
// tint the layer only if the decal wants to be tinted
layer - > SetAmbientColor ( hsColorRGBA ( ) . Set ( 0.f , 0.f , 0.f , 1.f ) ) ;
if ( decalChunk - > fTintDecal )
layer - > SetRuntimeColor ( hsColorRGBA ( ) . Set ( fCoverTint . r , fCoverTint . g , fCoverTint . b , 1.f ) ) ;
layer - > SetOpacity ( 1.f ) ;
// Blend flags, decals are alpha blended.
layer - > SetBlendFlags ( hsGMatState : : kBlendAlpha ) ;
// Only the bottom layer writes it's Z value.
layer - > SetZFlags ( hsGMatState : : kZNoZWrite ) ;
// No special shading.
layer - > SetShadeFlags ( hsGMatState : : kShadeReallyNoFog ) ;
// Clamp all textures.
layer - > SetClampFlags ( hsGMatState : : kClampTexture ) ;
// Draw passes individually.
layer - > SetMiscFlags ( hsGMatState : : kMiscRestartPassHere ) ;
// Shared UV coordinates.
layer - > SetUVWSrc ( 0 ) ;
// Set up the transform.
uint16_t x , y ;
x = decalChunk - > fAbsoluteX ;
y = decalChunk - > fAbsoluteY ;
if ( ( decalChunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kLeft )
x = ( uint16_t ) fPageLMargin ;
else if ( ( decalChunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kRight )
x = ( uint16_t ) ( baseMipmap - > GetWidth ( ) - decal - > GetWidth ( ) ) ;
else if ( ( decalChunk - > fFlags & pfEsHTMLChunk : : kAlignMask ) = = pfEsHTMLChunk : : kCenter )
x = ( uint16_t ) ( ( baseMipmap - > GetWidth ( ) > > 1 ) - ( decal - > GetWidth ( ) > > 1 ) ) ;
// x and y are in pixels, need to convert to a range of 0 to 1
float xRel = ( float ) x / ( float ) baseMipmap - > GetWidth ( ) ;
float yRel = ( float ) y / ( float ) baseMipmap - > GetHeight ( ) ;
// Need to convert the scaling to texture space
float xScale = ( float ) baseMipmap - > GetWidth ( ) / ( float ) decal - > GetWidth ( ) ;
float yScale = ( float ) baseMipmap - > GetHeight ( ) / ( float ) decal - > GetHeight ( ) ;
if ( decalChunk - > fNoResizeImg )
{
// book is NOT square, there is a h/w ratio of 1/0.7
// calc new size based on how the book has been skewed
xScale * = ( fWidthScale = = 0 ) ? 1 : ( 1 - fWidthScale ) ;
yScale * = ( fHeightScale = = 0 ) ? 1 : ( 1 - fHeightScale ) ;
yScale * = 1 / 0.7 ; // adjust because the book isn't square
}
hsVector3 scaleVec ( xScale , yScale , 1 ) , translateVec ( - ( xRel * xScale ) , - ( yRel * yScale ) , 0 ) , yTransVec ( 0 , - 1 , 0 ) , invertYVec ( 1 , - 1 , 1 ) ;
hsMatrix44 scaleMat , translateMat , invertY , transY ;
scaleMat . MakeScaleMat ( & scaleVec ) ;
translateMat . MakeTranslateMat ( & translateVec ) ;
invertY . MakeScaleMat ( & invertYVec ) ;
transY . MakeTranslateMat ( & yTransVec ) ;
hsMatrix44 xfm , flipY ;
flipY = invertY * transY ;
xfm = translateMat * scaleMat * flipY ;
layer - > SetTransform ( xfm ) ;
// Set the texture (assumes mipmap is non-nil).
plLayRefMsg * refMsg = new plLayRefMsg ( layer - > GetKey ( ) , plRefMsg : : kOnCreate , 0 , plLayRefMsg : : kTexture ) ;
hsgResMgr : : ResMgr ( ) - > SendRef ( decal - > GetKey ( ) , refMsg , plRefFlags : : kActiveRef ) ;
return plLayerInterface : : ConvertNoRef ( layer ) ;
}
void pfJournalBook : : ISetDecalLayers ( hsGMaterial * material , hsTArray < plLayerInterface * > layers )
{
// First, clear out the existing layers.
int i ;
for ( i = material - > GetNumLayers ( ) - 1 ; i > = 0 ; i - - )
{
plMatRefMsg * refMsg = new plMatRefMsg ( material - > GetKey ( ) , plRefMsg : : kOnRemove , i , plMatRefMsg : : kLayer ) ;
hsgResMgr : : ResMgr ( ) - > SendRef ( material - > GetLayer ( i ) - > GetKey ( ) , refMsg , plRefFlags : : kActiveRef ) ;
}
// Now append our new layers in order.
for ( i = 0 ; i < layers . GetCount ( ) ; i + + )
{
material - > AddLayerViaNotify ( layers [ i ] ) ;
}
}
//// IFindFontProps //////////////////////////////////////////////////////////
// Starting at the given chunk, works backwards to determine the full set of current
// font properties at that point, or assigns defaults if none were specified
void pfJournalBook : : IFindFontProps ( uint32_t chunkIdx , plString & face , uint8_t & size , uint8_t & flags , hsColorRGBA & color , int16_t & spacing )
{
enum Which
{
kFace = 0x01 ,
kSize = 0x02 ,
kFlags = 0x04 ,
kColor = 0x08 ,
kSpacing = 0x10 ,
kAllFound = kFace | kSize | kFlags | kColor | kSpacing
} ;
// Start empty
uint8_t found = 0 ;
// Work backwards and fill in our properties
chunkIdx + + ;
do
{
chunkIdx - - ;
if ( fHTMLSource . Count ( ) < = chunkIdx )
break ; // apparently it's sometimes possible for fHTMLSource to be empty (parse errors?)
pfEsHTMLChunk * chunk = fHTMLSource [ chunkIdx ] ;
if ( chunk - > fType = = pfEsHTMLChunk : : kFontChange )
{
// What do we (still) need?
if ( ! ( found & kFace ) & & chunk - > fText ! = L " " )
{
face = plString : : FromWchar ( chunk - > fText . c_str ( ) ) ;
found | = kFace ;
}
if ( ! ( found & kSize ) & & chunk - > fFontSize > 0 )
{
size = chunk - > fFontSize ;
found | = kSize ;
}
if ( ! ( found & kFlags ) & & ( chunk - > fFlags & pfEsHTMLChunk : : kFontMask ) ! = 0 )
{
flags = 0 ;
if ( chunk - > fFlags & pfEsHTMLChunk : : kFontBold )
flags | = plDynamicTextMap : : kFontBold ;
if ( chunk - > fFlags & pfEsHTMLChunk : : kFontItalic )
flags | = plDynamicTextMap : : kFontItalic ;
found | = kFlags ;
}
if ( ! ( found & kColor ) & & ( chunk - > fFlags & pfEsHTMLChunk : : kFontColor ) )
{
color = chunk - > fColor ;
found | = kColor ;
}
if ( ! ( found & kSpacing ) & & ( chunk - > fFlags & pfEsHTMLChunk : : kFontSpacing ) )
{
spacing = chunk - > fLineSpacing ;
found | = kSpacing ;
}
}
} while ( chunkIdx ! = 0 & & found ! = kAllFound ) ;
// Set any un-found defaults
if ( ! ( found & kFace ) )
face = " Arial " ;
if ( ! ( found & kSize ) )
size = 24 ;
if ( ! ( found & kFlags ) )
flags = 0 ;
if ( ! ( found & kColor ) )
color . Set ( 0.f , 0.f , 0.f , 1.f ) ;
if ( ! ( found & kSpacing ) )
spacing = 0 ;
}
//// IFindLastAlignment //////////////////////////////////////////////////////
// Find the last paragraph chunk and thus the last par alignment settings
uint8_t pfJournalBook : : IFindLastAlignment ( void ) const
{
int32_t idx ;
for ( idx = fHTMLSource . GetCount ( ) - 1 ; idx > = 0 ; idx - - )
{
if ( fHTMLSource [ idx ] - > fType = = pfEsHTMLChunk : : kParagraph & & fHTMLSource [ idx ] - > fFlags ! = 0 )
return ( uint8_t ) ( fHTMLSource [ idx ] - > fFlags ) ;
}
return pfEsHTMLChunk : : kLeft ;
}
//// IRecalcPageStarts ///////////////////////////////////////////////////////
// Ensures that all the page starts are calced up to the given page (but not including it)
void pfJournalBook : : IRecalcPageStarts ( uint32_t upToPage )
{
uint32_t page ;
// Well, sadly, we can't really calc the page starts without at least a DTMap
// we can change things on...so we just pick one and render. Note: this WILL
// trash the font settings on the given DTMap!
// We assume that the stored page starts we already have are accurate, so
// just start from there and calc onward
for ( page = fPageStarts . GetCount ( ) - 1 ; page < upToPage & & page < = fLastPage ; page + + )
{
// normally we would surpress rendering the pages, but that seems to have a bug in it
// that causes lost text that the rendering doesn't have. Since it isn't very costly to
// actually draw them all (even in large journals), we're just going to do it
IRenderPage ( page , pfJournalDlgProc : : kTagTurnBackDTMap , false ) ;
// Reset any "visible" links since they aren't really visible
uint16_t i ;
for ( i = 0 ; i < fVisibleLinks . Count ( ) ; i + + )
{
fVisibleLinks [ i ] - > fLinkRect . Set ( 0 , 0 , 0 , 0 ) ;
}
}
}
//// ISendNotify /////////////////////////////////////////////////////////////
// Just sends out a notify to our currently set receiver key
void pfJournalBook : : ISendNotify ( uint32_t type , uint32_t linkID )
{
if ( fCallbackKey ! = nil )
{
plNotifyMsg * pMsg = new plNotifyMsg ;
pMsg - > AddBookEvent ( type , linkID ) ;
pMsg - > SetBCastFlag ( plMessage : : kNetPropagate , false ) ; // don't deliver networked!
pMsg - > Send ( fCallbackKey ) ;
}
}
//// SetBookSize /////////////////////////////////////////////////////////////
// Sets the book size scaling. 1,1 would be full size, 0,0 is the smallest size possible
// Note: internally we store these as the seek positions on our animations,
// so the incoming parameters are actually inverse of what we finally want
void pfJournalBook : : SetBookSize ( float width , float height )
{
fWidthScale = 1.f - width ;
fHeightScale = 1.f - height ;
if ( fBookGUIs [ fCurBookGUI ] - > CurBook ( ) = = this )
fBookGUIs [ fCurBookGUI ] - > SetCurrSize ( fWidthScale , fHeightScale ) ;
}
//// ILoadAllImages //////////////////////////////////////////////////////////
// Load (or unload) all the images for the book
void pfJournalBook : : ILoadAllImages ( bool unload )
{
uint32_t i ;
// load the cover
if ( fCoverFromHTML & & fCoverMipKey ! = nil )
{
if ( unload )
fBookGUIs [ fCurBookGUI ] - > GetKey ( ) - > Release ( fCoverMipKey ) ;
else
{
plGenRefMsg * ref = new plGenRefMsg ( fBookGUIs [ fCurBookGUI ] - > GetKey ( ) , plRefMsg : : kOnCreate , - 1 , kRefImage ) ;
hsgResMgr : : ResMgr ( ) - > AddViaNotify ( fCoverMipKey , ref , plRefFlags : : kActiveRef ) ;
}
}
for ( i = 0 ; i < fHTMLSource . GetCount ( ) ; i + + )
{
if ( fHTMLSource [ i ] - > fType = = pfEsHTMLChunk : : kImage & & fHTMLSource [ i ] - > fImageKey ! = nil )
{
if ( unload )
fBookGUIs [ fCurBookGUI ] - > GetKey ( ) - > Release ( fHTMLSource [ i ] - > fImageKey ) ;
else
{
plGenRefMsg * ref = new plGenRefMsg ( fBookGUIs [ fCurBookGUI ] - > GetKey ( ) , plRefMsg : : kOnCreate , - 1 , kRefImage ) ;
hsgResMgr : : ResMgr ( ) - > AddViaNotify ( fHTMLSource [ i ] - > fImageKey , ref , plRefFlags : : kActiveRef ) ;
}
}
}
}
void pfJournalBook : : IPurgeDynaTextMaps ( )
{
plDynamicTextMap * turnFront = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagTurnFrontDTMap ) ;
if ( turnFront )
turnFront - > PurgeImage ( ) ;
plDynamicTextMap * right = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagRightDTMap ) ;
if ( right )
right - > PurgeImage ( ) ;
plDynamicTextMap * turnBack = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagTurnBackDTMap ) ;
if ( turnBack )
turnBack - > PurgeImage ( ) ;
plDynamicTextMap * left = fBookGUIs [ fCurBookGUI ] - > GetDTMap ( pfJournalDlgProc : : kTagLeftDTMap ) ;
if ( left )
left - > PurgeImage ( ) ;
pfGUIMultiLineEditCtrl * leftEdit = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) ;
if ( leftEdit )
leftEdit - > PurgeDynaTextMapImage ( ) ;
pfGUIMultiLineEditCtrl * rightEdit = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagRightEditCtrl ) ;
if ( rightEdit )
rightEdit - > PurgeDynaTextMapImage ( ) ;
pfGUIMultiLineEditCtrl * turnFrontEdit = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnFrontEditCtrl ) ;
if ( turnFrontEdit )
turnFrontEdit - > PurgeDynaTextMapImage ( ) ;
pfGUIMultiLineEditCtrl * turnBackEdit = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagTurnBackEditCtrl ) ;
if ( turnBackEdit )
turnBackEdit - > PurgeDynaTextMapImage ( ) ;
}
std : : string pfJournalBook : : GetEditableText ( )
{
pfGUIMultiLineEditCtrl * left = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) ;
if ( left )
{
char * temp = left - > GetNonCodedBuffer ( ) ;
std : : string retVal = temp ;
delete [ ] temp ;
return retVal ;
}
return " " ;
}
void pfJournalBook : : SetEditableText ( std : : string text )
{
pfGUIMultiLineEditCtrl * left = fBookGUIs [ fCurBookGUI ] - > GetEditCtrl ( pfJournalDlgProc : : kTagLeftEditCtrl ) ;
if ( left )
{
left - > SetBuffer ( text . c_str ( ) ) ;
left - > ForceUpdate ( ) ;
}
}