You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3614 lines
116 KiB
3614 lines
116 KiB
4 years ago
|
/*==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 3ds 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, 3ds 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 "hsUtils.h"
|
||
|
#include "hsStlUtils.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 "../plPipeline/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"
|
||
|
#ifdef USE_BINK_SDK
|
||
|
#include "../pfSurface/plLayerBink.h"
|
||
|
#endif
|
||
|
|
||
|
// 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 fFontSize;
|
||
|
UInt32 fFlags;
|
||
|
UInt8 fType;
|
||
|
UInt32 fEventID;
|
||
|
|
||
|
pcSmallRect fLinkRect; // Used only for image chunks, and only when stored in the fVisibleLinks array
|
||
|
|
||
|
hsColorRGBA fColor;
|
||
|
|
||
|
UInt16 fAbsoluteX, fAbsoluteY;
|
||
|
|
||
|
hsScalar fCurrOpacity; // For SFX images
|
||
|
hsScalar fSFXTime; // For SFX images
|
||
|
hsScalar fMinOpacity, fMaxOpacity;
|
||
|
|
||
|
hsColorRGBA fCurrColor;
|
||
|
hsColorRGBA fOffColor, fOnColor;
|
||
|
|
||
|
bool fNoResizeImg;
|
||
|
Int16 fLineSpacing;
|
||
|
bool fTintDecal;
|
||
|
bool fLoopMovie;
|
||
|
bool fOnCover; // if true, the movie is on the cover
|
||
|
UInt8 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 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 size, UInt32 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 event )
|
||
|
{
|
||
|
if (fBook)
|
||
|
{
|
||
|
if( ctrl == fBook->fLeftPageMap )
|
||
|
{
|
||
|
if( event == pfGUIClickMapCtrl::kMouseHovered )
|
||
|
{
|
||
|
if (fBook->fCurrBook)
|
||
|
{
|
||
|
// Update our custom cursor on the map
|
||
|
Int32 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 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 cursorPos) { bookData->HitEndOfControlList(cursorPos); }
|
||
|
virtual void OnBeginningOfControlList(Int32 cursorPos) { bookData->HitBeginningOfControlList(cursorPos); }
|
||
|
};
|
||
|
|
||
|
//// Book data class /////////////////////////////////////////////////////////
|
||
|
|
||
|
pfBookData::pfBookData(const char *guiName /* = nil */)
|
||
|
{
|
||
|
fCurrBook = nil;
|
||
|
fDialog = nil;
|
||
|
fCoverButton = fTurnPageButton = nil;
|
||
|
fLeftPageMap = fRightPageMap = nil;
|
||
|
fCoverLayer = nil;
|
||
|
fCoverMaterial = nil;
|
||
|
UInt16 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)
|
||
|
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());
|
||
|
}
|
||
|
|
||
|
hsBool 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( (hsBool)( callback->fUser & 0x01 ) ? true : false );
|
||
|
else if( callback->fUser & 0x02 )
|
||
|
StartTriggeredFlip( (hsBool)( callback->fUser & 0x01 ) ? true : false );
|
||
|
else
|
||
|
IFinishTriggeredFlip( (hsBool)( 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( (hsScalar)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(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefDialog), plRefFlags::kPassiveRef);
|
||
|
|
||
|
// Hijack the dialog proc with our own
|
||
|
templateDlg->SetHandlerForAll(TRACKED_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(), TRACKED_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(TRACKED_NEW pfBookMultiLineEditProc(this));
|
||
|
fTurnFrontEditCtrl->SetNext(fTurnBackEditCtrl); // these are always back to back
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//// IGetDTMap ///////////////////////////////////////////////////////////////
|
||
|
// Just a quick helper
|
||
|
|
||
|
plDynamicTextMap *pfBookData::GetDTMap(UInt32 which)
|
||
|
{
|
||
|
pfGUIDynDisplayCtrl *display = pfGUIDynDisplayCtrl::ConvertNoRef(fDialog->GetControlFromTag(which));
|
||
|
return display->GetMap(0);
|
||
|
}
|
||
|
|
||
|
//// GetEditCtrl /////////////////////////////////////////////////////////////
|
||
|
|
||
|
pfGUIMultiLineEditCtrl *pfBookData::GetEditCtrl(UInt32 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(hsScalar 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
|
||
|
hsScalar deltaT = currTime - fBaseSFXTime;
|
||
|
|
||
|
UInt32 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 isOdd = 0;
|
||
|
hsScalar 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(hsBool 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 = TRACKED_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(hsBool flipBackwards, hsBool 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();
|
||
|
const char *animName = fTurnPageButton->GetAnimationName();
|
||
|
|
||
|
plAnimCmdMsg *msg = TRACKED_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 = TRACKED_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 = TRACKED_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(hsBool 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(hsBool 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 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);
|
||
|
#ifdef USE_BINK_SDK
|
||
|
// move the videos over
|
||
|
fCurrBook->IMoveMovies(PageMaterial(kTurnFrontPage),PageMaterial(kRightPage));
|
||
|
#endif // USE_BINK_SDK
|
||
|
}
|
||
|
}
|
||
|
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);
|
||
|
#ifdef USE_BINK_SDK
|
||
|
// move the videos over
|
||
|
fCurrBook->IMoveMovies(PageMaterial(kTurnBackPage),PageMaterial(kLeftPage));
|
||
|
#endif // USE_BINK_SDK
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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(hsScalar w, hsScalar h)
|
||
|
{
|
||
|
fWidthCtrl->SetCurrValue(w);
|
||
|
fHeightCtrl->SetCurrValue(h);
|
||
|
}
|
||
|
|
||
|
//// PlayBookCloseAnim //////////////////////////////////////////////////////
|
||
|
// Triggers our animation for closing or opening the book.
|
||
|
|
||
|
void pfBookData::PlayBookCloseAnim(hsBool closeIt /*= true*/, hsBool 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 cursorPos)
|
||
|
{
|
||
|
fAdjustCursorTo = cursorPos;
|
||
|
if (fCurrBook)
|
||
|
fCurrBook->NextPage();
|
||
|
}
|
||
|
|
||
|
void pfBookData::HitBeginningOfControlList(Int32 cursorPos)
|
||
|
{
|
||
|
fAdjustCursorTo = cursorPos;
|
||
|
if (fCurrBook)
|
||
|
fCurrBook->PreviousPage();
|
||
|
}
|
||
|
|
||
|
void pfBookData::EnableEditGUI(hsBool 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<std::string,pfBookData*> pfJournalBook::fBookGUIs;
|
||
|
|
||
|
void pfJournalBook::SingletonInit( void )
|
||
|
{
|
||
|
fBookGUIs["BkBook"] = TRACKED_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<std::string,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 char *guiName )
|
||
|
{
|
||
|
if (fBookGUIs.find(guiName) == fBookGUIs.end()) // is it already loaded?
|
||
|
{ // nope, load it
|
||
|
fBookGUIs[guiName] = TRACKED_NEW pfBookData(guiName);
|
||
|
hsgResMgr::ResMgr()->NewKey(guiName,fBookGUIs[guiName],pfGameGUIMgr::GetInstance()->GetKey()->GetUoid().GetLocation());
|
||
|
fBookGUIs[guiName]->LoadGUI();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void pfJournalBook::UnloadGUI( const char *guiName )
|
||
|
{
|
||
|
if (strcmp(guiName,"BkBook")==0)
|
||
|
return; // do not allow people to unload the default book gui
|
||
|
std::map<std::string,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<std::string,pfBookData*>::iterator i = fBookGUIs.begin();
|
||
|
std::vector<std::string> names;
|
||
|
while (i != fBookGUIs.end())
|
||
|
{
|
||
|
std::string 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].c_str()); // 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 char *guiName /* = nil */ )
|
||
|
{
|
||
|
if (guiName && (strcmp(guiName,"") != 0))
|
||
|
fCurBookGUI = guiName;
|
||
|
else
|
||
|
fCurBookGUI = "BkBook";
|
||
|
if (fBookGUIs.find(fCurBookGUI) == fBookGUIs.end())
|
||
|
{
|
||
|
fBookGUIs[fCurBookGUI] = TRACKED_NEW pfBookData(fCurBookGUI.c_str());
|
||
|
hsgResMgr::ResMgr()->NewKey(fCurBookGUI.c_str(),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 char *guiName /* = nil */ )
|
||
|
{
|
||
|
if (guiName && (strcmp(guiName,"") != 0))
|
||
|
fCurBookGUI = guiName;
|
||
|
else
|
||
|
fCurBookGUI = "BkBook";
|
||
|
if (fBookGUIs.find(fCurBookGUI) == fBookGUIs.end())
|
||
|
{
|
||
|
fBookGUIs[fCurBookGUI] = TRACKED_NEW pfBookData(fCurBookGUI.c_str());
|
||
|
hsgResMgr::ResMgr()->NewKey(fCurBookGUI.c_str(),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 //////////////////////////////////////////////////////////////
|
||
|
|
||
|
hsBool pfJournalBook::MsgReceive( plMessage *pMsg )
|
||
|
{
|
||
|
return hsKeyedObject::MsgReceive( pMsg );
|
||
|
}
|
||
|
|
||
|
void pfJournalBook::SetGUI( const char *guiName )
|
||
|
{
|
||
|
if (guiName && (strcmp(guiName,"") != 0))
|
||
|
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( hsBool 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));
|
||
|
}
|
||
|
#ifdef USE_BINK_SDK
|
||
|
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)
|
||
|
plLayerBink *movieLayer = IMakeMovieLayer(fCoverDecals[i],0,0,mip,pfJournalDlgProc::kTagLeftDTMap,false);
|
||
|
loadedMovie *movie = TRACKED_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
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
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( hsBool 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!)
|
||
|
#ifdef USE_BINK_SDK
|
||
|
int i;
|
||
|
for( i = 0; i < fLoadedMovies.GetCount(); i++ )
|
||
|
{
|
||
|
plLayerBink *movie = fLoadedMovies[ i ]->movieLayer;
|
||
|
movie->GetKey()->UnRefObject();
|
||
|
delete fLoadedMovies[ i ];
|
||
|
}
|
||
|
fLoadedMovies.Reset();
|
||
|
#endif // USE_BINK_SDK
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//// Open ////////////////////////////////////////////////////////////////////
|
||
|
// Opens the book, optionally to the given page
|
||
|
|
||
|
void pfJournalBook::Open( UInt32 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( hsBool closeNotOpen, hsBool 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();
|
||
|
const char *animName = fBookGUIs[fCurBookGUI]->CoverButton()->GetAnimationName();
|
||
|
|
||
|
plAnimCmdMsg *msg = TRACKED_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 = TRACKED_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 );
|
||
|
}
|
||
|
#ifdef USE_BINK_SDK
|
||
|
// copy the videos over
|
||
|
IMoveMovies( fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kRightPage), fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kTurnFrontPage) );
|
||
|
#endif // USE_BINK_SDK
|
||
|
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 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 );
|
||
|
}
|
||
|
#ifdef USE_BINK_SDK
|
||
|
// copy the videos over
|
||
|
IMoveMovies( fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kLeftPage), fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kTurnBackPage) );
|
||
|
#endif // USE_BINK_SDK
|
||
|
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 pfJournalBook::IFindCurrVisibleLink( hsBool rightNotLeft, hsBool 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 *= (hsScalar)dtMap->GetWidth();
|
||
|
pt.fY *= (hsScalar)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 i;
|
||
|
for( i = 0; i < fVisibleLinks.GetCount(); i++ )
|
||
|
{
|
||
|
if( fVisibleLinks[ i ]->fLinkRect.Contains( (Int16)pt.fX, (Int16)pt.fY ) )
|
||
|
{
|
||
|
// Found a visible link
|
||
|
return (Int32)i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
//// IHandleLeftSideClick ////////////////////////////////////////////////////
|
||
|
|
||
|
void pfJournalBook::IHandleLeftSideClick( void )
|
||
|
{
|
||
|
if( fBookGUIs[fCurBookGUI]->CurrentlyTurning() )
|
||
|
return;
|
||
|
|
||
|
Int32 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 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 idx, pfBookData::WhichSide which )
|
||
|
{
|
||
|
// Special processing for checkboxes--toggle our state, switch our opacity
|
||
|
// and then send a notify about our new state
|
||
|
hsBool 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 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(hsBool 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 IConvertHex( const wchar_t *str )
|
||
|
{
|
||
|
UInt32 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
|
||
|
|
||
|
hsBool pfJournalBook::ICompileSource( const wchar_t *source, const plLocation &hintLoc )
|
||
|
{
|
||
|
IFreeSource();
|
||
|
|
||
|
|
||
|
pfEsHTMLChunk *chunk, *lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil );
|
||
|
const wchar_t *c, *start;
|
||
|
wchar_t name[ 128 ], option[ 256 ];
|
||
|
float bookWidth=1.0, bookHeight=1.0;
|
||
|
UInt8 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 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 count = ((UInt32)c - (UInt32)start)/2; // wchar_t is 2 bytes
|
||
|
|
||
|
wchar_t *temp = TRACKED_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 = TRACKED_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 = TRACKED_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 = _wtoi( option );
|
||
|
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 = _wtoi( comma + 1 );
|
||
|
*comma = 0;
|
||
|
}
|
||
|
chunk->fAbsoluteX = _wtoi( option );
|
||
|
}
|
||
|
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 = (hsScalar)atof( comma2 + 1 );
|
||
|
*comma2 = 0;
|
||
|
}
|
||
|
chunk->fMinOpacity = (hsScalar)atof( comma + 1 );
|
||
|
*comma = 0;
|
||
|
}
|
||
|
chunk->fSFXTime = (hsScalar)atof( cOption );
|
||
|
delete [] cOption;
|
||
|
}
|
||
|
else if( wcsicmp( name, L"opacity" ) == 0 )
|
||
|
{
|
||
|
chunk->fFlags |= pfEsHTMLChunk::kTranslucent;
|
||
|
char *cOption = hsWStringToString(option);
|
||
|
chunk->fCurrOpacity = (hsScalar)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( _wtoi( comma2 + 1 ) != 0 )
|
||
|
chunk->fFlags |= pfEsHTMLChunk::kChecked;
|
||
|
*comma2 = 0;
|
||
|
}
|
||
|
UInt32 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 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 = TRACKED_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 = TRACKED_NEW pfEsHTMLChunk( nil );
|
||
|
lastParChunk->fFlags = IFindLastAlignment();
|
||
|
break;
|
||
|
|
||
|
case pfEsHTMLChunk::kPageBreak:
|
||
|
c += 3;
|
||
|
chunk = TRACKED_NEW pfEsHTMLChunk();
|
||
|
while( IGetNextOption( c, name, option ) )
|
||
|
{
|
||
|
}
|
||
|
fHTMLSource.Append( chunk );
|
||
|
// Start new paragraph chunk after this one
|
||
|
lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil );
|
||
|
lastParChunk->fFlags = IFindLastAlignment();
|
||
|
break;
|
||
|
|
||
|
case pfEsHTMLChunk::kFontChange:
|
||
|
c += 5;
|
||
|
chunk = TRACKED_NEW pfEsHTMLChunk( nil, 0, 0 );
|
||
|
while( IGetNextOption( c, name, option ) )
|
||
|
{
|
||
|
if( wcsicmp( name, L"style" ) == 0 )
|
||
|
{
|
||
|
UInt8 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 = _wtoi( option );
|
||
|
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 = _wtoi(option);
|
||
|
chunk->fFlags |= pfEsHTMLChunk::kFontSpacing;
|
||
|
}
|
||
|
}
|
||
|
fHTMLSource.Append( chunk );
|
||
|
// Start new paragraph chunk after this one
|
||
|
lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil );
|
||
|
lastParChunk->fFlags = IFindLastAlignment();
|
||
|
break;
|
||
|
|
||
|
case pfEsHTMLChunk::kMargin:
|
||
|
c += 7;
|
||
|
while(IGetNextOption(c,name,option))
|
||
|
{
|
||
|
if (wcsicmp(name,L"top") == 0)
|
||
|
fPageTMargin = _wtoi(option);
|
||
|
else if (wcsicmp(name,L"left") == 0)
|
||
|
fPageLMargin = _wtoi(option);
|
||
|
else if (wcsicmp(name,L"bottom") == 0)
|
||
|
fPageBMargin = _wtoi(option);
|
||
|
else if (wcsicmp(name,L"right") == 0)
|
||
|
fPageRMargin = _wtoi(option);
|
||
|
}
|
||
|
// 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 = TRACKED_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 = TRACKED_NEW pfEsHTMLChunk( nil );
|
||
|
lastParChunk->fFlags = IFindLastAlignment();
|
||
|
break;
|
||
|
|
||
|
case pfEsHTMLChunk::kDecal:
|
||
|
c += 6;
|
||
|
chunk = TRACKED_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 = _wtoi( comma + 1 );
|
||
|
*comma = 0;
|
||
|
}
|
||
|
chunk->fAbsoluteX = _wtoi( option );
|
||
|
}
|
||
|
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 = TRACKED_NEW pfEsHTMLChunk( nil );
|
||
|
lastParChunk->fFlags = IFindLastAlignment();
|
||
|
break;
|
||
|
|
||
|
case pfEsHTMLChunk::kMovie:
|
||
|
c += 6;
|
||
|
chunk = TRACKED_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 = _wtoi( option );
|
||
|
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 = _wtoi( comma + 1 );
|
||
|
*comma = 0;
|
||
|
}
|
||
|
chunk->fAbsoluteX = _wtoi( option );
|
||
|
}
|
||
|
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 = TRACKED_NEW pfEsHTMLChunk( nil );
|
||
|
lastParChunk->fFlags = IFindLastAlignment();
|
||
|
break;
|
||
|
|
||
|
case pfEsHTMLChunk::kEditable:
|
||
|
c += 9;
|
||
|
SetEditable(true);
|
||
|
chunk = TRACKED_NEW pfEsHTMLChunk();
|
||
|
while( IGetNextOption( c, name, option ) )
|
||
|
{
|
||
|
}
|
||
|
fHTMLSource.Append( chunk );
|
||
|
// Start new paragraph chunk after this one
|
||
|
lastParChunk = TRACKED_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 count = (UInt32)c - (UInt32)start;
|
||
|
|
||
|
wchar_t *temp = TRACKED_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 pfJournalBook::IGetTagType( const wchar_t *string )
|
||
|
{
|
||
|
if( string[ 0 ] != '<' )
|
||
|
return pfEsHTMLChunk::kEmpty;
|
||
|
|
||
|
struct TagRec
|
||
|
{
|
||
|
const wchar_t *fTag;
|
||
|
UInt8 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 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;
|
||
|
}
|
||
|
|
||
|
hsBool 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 len = ((UInt32)string - (UInt32)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 = ((UInt32)string - (UInt32)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 = ((UInt32)string - (UInt32)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 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();
|
||
|
|
||
|
#ifdef USE_BINK_SDK
|
||
|
for( i = 0; i < fLoadedMovies.GetCount(); i++ )
|
||
|
{
|
||
|
plLayerBink *movie = fLoadedMovies[ i ]->movieLayer;
|
||
|
movie->GetKey()->UnRefObject();
|
||
|
delete fLoadedMovies[ i ];
|
||
|
}
|
||
|
fLoadedMovies.Reset();
|
||
|
#endif // USE_BINK_SDK
|
||
|
}
|
||
|
|
||
|
//// 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 "../plJPEG/plJPEG.h"
|
||
|
#endif
|
||
|
|
||
|
plKey pfJournalBook::IGetMipmapKey( const wchar_t *name, const plLocation &loc )
|
||
|
{
|
||
|
char *cName = hsWStringToString(name);
|
||
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
||
|
if( strchr( cName, '/' ) != nil || strchr( cName, '\\' ) != nil )
|
||
|
{
|
||
|
// For internal use only--we allow local path names of JPEG images, to
|
||
|
// facilitate fast prototyping
|
||
|
plMipmap *mip = plJPEG::Instance().ReadFromFile( cName );
|
||
|
hsgResMgr::ResMgr()->NewKey( cName, mip, loc );
|
||
|
delete [] cName;
|
||
|
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 )
|
||
|
{
|
||
|
delete [] cName;
|
||
|
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 )
|
||
|
{
|
||
|
delete [] cName;
|
||
|
return key;
|
||
|
}
|
||
|
|
||
|
// Do a search through our current age with just the name given
|
||
|
if( plNetClientMgr::GetInstance() != nil )
|
||
|
{
|
||
|
const char *thisAge = plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeName();
|
||
|
if( thisAge != nil )
|
||
|
{
|
||
|
key = plKeyFinder::Instance().StupidSearch( thisAge, nil, plMipmap::Index(), cName, true );
|
||
|
if( key != nil )
|
||
|
{
|
||
|
delete [] cName;
|
||
|
return key;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] cName;
|
||
|
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 page, UInt32 whichDTMap, hsBool 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);
|
||
|
|
||
|
#ifdef USE_BINK_SDK
|
||
|
if (material)
|
||
|
{
|
||
|
// clear any exiting layers (movies) from the material
|
||
|
int i;
|
||
|
for( i = 0; i < material->GetNumLayers(); i++ ) // remove all plLayerBink layers
|
||
|
{
|
||
|
plLayerInterface *matLayer = material->GetLayer(i);
|
||
|
plLayerBink *bink = plLayerBink::ConvertNoRef(matLayer);
|
||
|
if (bink) // if it was a bink layer
|
||
|
{
|
||
|
plMatRefMsg* refMsg = TRACKED_NEW plMatRefMsg(material->GetKey(), plRefMsg::kOnRemove, i, plMatRefMsg::kLayer); // remove it
|
||
|
hsgResMgr::ResMgr()->SendRef(material->GetLayer(i)->GetKey(), refMsg, plRefFlags::kActiveRef);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // USE_BINK_SDK
|
||
|
|
||
|
hsAssert(page < fPageStarts.GetCount() || page > fLastPage, "UnInitialized page start!");
|
||
|
if( page <= fLastPage
|
||
|
&& page < fPageStarts.GetCount()) // Added this as a crash-prevention bandaid - MT
|
||
|
{
|
||
|
UInt32 idx;
|
||
|
UInt16 width, height, y, x, ascent, lastX, lastY;
|
||
|
|
||
|
UInt8 fontFlags, fontSize;
|
||
|
const wchar_t *fontFace;
|
||
|
hsColorRGBA fontColor;
|
||
|
Int16 fontSpacing;
|
||
|
hsBool 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)fPageLMargin, y = (UInt16)fPageTMargin;
|
||
|
y < (UInt16)(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)fPageLMargin; // reset X if our justification changes
|
||
|
}
|
||
|
else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kRight )
|
||
|
{
|
||
|
dtMap->SetJustify( plDynamicTextMap::kRightJustify );
|
||
|
x = (UInt16)fPageLMargin; // reset X if our justification changes
|
||
|
}
|
||
|
else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kCenter )
|
||
|
{
|
||
|
dtMap->SetJustify( plDynamicTextMap::kCenter );
|
||
|
x = (UInt16)fPageLMargin; // reset X if our justification changes
|
||
|
}
|
||
|
|
||
|
dtMap->SetFirstLineIndent( (Int16)(x - fPageLMargin) );
|
||
|
width = (UInt16)(512 - fPageLMargin - fPageRMargin);
|
||
|
height = (UInt16)(512 - fPageBMargin - y);
|
||
|
UInt32 lastChar;
|
||
|
dtMap->CalcWrappedStringSize( chunk->fText.c_str(), &width, &height, &lastChar, &ascent, &lastX, &lastY );
|
||
|
width = (UInt16)(512 - fPageLMargin - fPageRMargin);
|
||
|
if( !suppressRendering )
|
||
|
dtMap->DrawWrappedString( (UInt16)fPageLMargin, y, chunk->fText.c_str(), width, (UInt16)(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( 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 = TRACKED_NEW wchar_t[fTextLen+1];
|
||
|
wcscpy(s,chunk->fText.c_str());
|
||
|
s[fTextLen] = L'\0';
|
||
|
|
||
|
// Note: Makes a copy of the string
|
||
|
pfEsHTMLChunk *c2 = TRACKED_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)(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)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)(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)fPageLMargin;
|
||
|
else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kRight )
|
||
|
x = (UInt16)(512 - fPageRMargin - mip->GetWidth());
|
||
|
else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kCenter )
|
||
|
x = (UInt16)(256 - ( mip->GetWidth() >> 1 ));
|
||
|
|
||
|
IDrawMipmap( chunk, x, y, mip, dtMap, whichDTMap, suppressRendering );
|
||
|
|
||
|
y += (UInt16)(mip->GetHeight());
|
||
|
x = (UInt16)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)(512 - fPageTMargin - fPageBMargin);
|
||
|
x = (UInt16)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:
|
||
|
#ifdef USE_BINK_SDK
|
||
|
movieAlreadyLoaded = (IMovieAlreadyLoaded(chunk) != nil); // have we already cached it?
|
||
|
plLayerBink *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 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)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)movieHeight;
|
||
|
x = (UInt16)fPageLMargin;
|
||
|
}
|
||
|
if (!movieAlreadyLoaded) // if the movie wasn't already cached, cache it
|
||
|
{
|
||
|
movie = TRACKED_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);
|
||
|
}
|
||
|
#endif // USE_BINK_SDK
|
||
|
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 /////////////////////////////////////////////////////////////
|
||
|
|
||
|
#ifdef USE_BINK_SDK
|
||
|
void pfJournalBook::IMoveMovies( hsGMaterial *source, hsGMaterial *dest )
|
||
|
{
|
||
|
hsTArray<plLayerBink*> 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 plLayerBink layers
|
||
|
{
|
||
|
plLayerInterface *matLayer = source->GetLayer(i);
|
||
|
plLayerBink *bink = plLayerBink::ConvertNoRef(matLayer);
|
||
|
if (bink) // if it was a bink layer
|
||
|
{
|
||
|
plMatRefMsg* refMsg = TRACKED_NEW plMatRefMsg(source->GetKey(), plRefMsg::kOnRemove, i, plMatRefMsg::kLayer); // remove it
|
||
|
hsgResMgr::ResMgr()->SendRef(source->GetLayer(i)->GetKey(), refMsg, plRefFlags::kActiveRef);
|
||
|
moviesOnPage.Append(bink);
|
||
|
}
|
||
|
}
|
||
|
// clear the destination's movies (if it has any)
|
||
|
for( i = 0; i < dest->GetNumLayers(); i++ ) // remove all plLayerBink layers
|
||
|
{
|
||
|
plLayerInterface *matLayer = dest->GetLayer(i);
|
||
|
plLayerBink *bink = plLayerBink::ConvertNoRef(matLayer);
|
||
|
if (bink) // if it was a bink layer
|
||
|
{
|
||
|
plMatRefMsg* refMsg = TRACKED_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]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // USE_BINK_SDK
|
||
|
|
||
|
//// IDrawMipmap /////////////////////////////////////////////////////////////
|
||
|
|
||
|
void pfJournalBook::IDrawMipmap( pfEsHTMLChunk *chunk, UInt16 x, UInt16 y, plMipmap *mip, plDynamicTextMap *dtMap, UInt32 whichDTMap, hsBool dontRender )
|
||
|
{
|
||
|
plMipmap *copy = TRACKED_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 width = (UInt32)(mip->GetWidth()*xScale);
|
||
|
UInt32 height = (UInt32)(mip->GetHeight()*yScale);
|
||
|
UInt16 xShift;
|
||
|
UInt16 yShift;
|
||
|
|
||
|
if (dtMap->GetWidth() < width) width = dtMap->GetWidth();
|
||
|
if (dtMap->GetHeight() < height) height = dtMap->GetHeight();
|
||
|
|
||
|
if (height < mip->GetHeight())
|
||
|
{
|
||
|
yShift = (UInt16)((mip->GetHeight()-height)/2);
|
||
|
if (y+yShift+height > dtMap->GetHeight())
|
||
|
y = (UInt16)(dtMap->GetHeight()-height);
|
||
|
else
|
||
|
y += yShift;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
yShift = (UInt16)((height-mip->GetHeight())/2);
|
||
|
if (yShift > y)
|
||
|
y = 0;
|
||
|
else
|
||
|
y -= yShift;
|
||
|
}
|
||
|
|
||
|
if (width < mip->GetWidth())
|
||
|
{
|
||
|
xShift = (UInt16)((mip->GetWidth()-width)/2);
|
||
|
if (x+xShift+width > dtMap->GetWidth())
|
||
|
x = (UInt16)(dtMap->GetWidth()-width);
|
||
|
else
|
||
|
x += xShift;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
xShift = (UInt16)((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)width,(UInt16)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)(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)(chunk->fCurrOpacity * 255.f);
|
||
|
}
|
||
|
dtMap->Composite( copy, x, y, &opts );
|
||
|
}
|
||
|
|
||
|
if( chunk->fFlags & pfEsHTMLChunk::kCanLink )
|
||
|
{
|
||
|
if( whichDTMap == pfJournalDlgProc::kTagRightDTMap || whichDTMap == pfJournalDlgProc::kTagTurnFrontDTMap )
|
||
|
x += (UInt16)(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)(copy->GetWidth()), (Int16)(copy->GetHeight()) );
|
||
|
fVisibleLinks.Append( chunk );
|
||
|
}
|
||
|
delete copy;
|
||
|
}
|
||
|
|
||
|
#ifdef USE_BINK_SDK
|
||
|
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;
|
||
|
}
|
||
|
#endif // USE_BINK_SDK
|
||
|
|
||
|
plKey pfJournalBook::GetMovie(UInt8 index)
|
||
|
{
|
||
|
#ifdef USE_BINK_SDK
|
||
|
loadedMovie *movie = IGetMovieByIndex(index);
|
||
|
if (movie)
|
||
|
return movie->movieLayer->GetKey();
|
||
|
#endif // USE_BINK_SDK
|
||
|
return plKey(nil);
|
||
|
}
|
||
|
|
||
|
#ifdef USE_BINK_SDK
|
||
|
pfJournalBook::loadedMovie *pfJournalBook::IGetMovieByIndex(UInt8 index)
|
||
|
{
|
||
|
int i;
|
||
|
for (i=0; i<fLoadedMovies.GetCount(); i++)
|
||
|
{
|
||
|
if (fLoadedMovies[i]->movieChunk->fMovieIndex == index)
|
||
|
return fLoadedMovies[i];
|
||
|
}
|
||
|
return nil;
|
||
|
}
|
||
|
#endif // USE_BINK_SDK
|
||
|
|
||
|
#ifdef USE_BINK_SDK
|
||
|
plLayerBink *pfJournalBook::IMakeMovieLayer(pfEsHTMLChunk *chunk, UInt16 x, UInt16 y, plMipmap *baseMipmap, UInt32 whichDTMap, hsBool dontRender)
|
||
|
{
|
||
|
// see if it's already loaded
|
||
|
loadedMovie *movie = IMovieAlreadyLoaded(chunk);
|
||
|
plLayer* layer = nil;
|
||
|
plLayerBink* movieLayer = nil;
|
||
|
UInt16 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;
|
||
|
char buff[256];
|
||
|
|
||
|
sprintf(buff, "%s_%d_ml", GetKey()->GetName(), uniqueSuffix);
|
||
|
layer = TRACKED_NEW plLayer;
|
||
|
hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation());
|
||
|
|
||
|
sprintf(buff, "%s_%d_m", GetKey()->GetName(), uniqueSuffix++);
|
||
|
movieLayer = TRACKED_NEW plLayerBink;
|
||
|
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)fPageLMargin;
|
||
|
else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kRight )
|
||
|
x = (UInt16)(baseMipmap->GetWidth() - fPageRMargin - movieWidth);
|
||
|
else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kCenter )
|
||
|
x = (UInt16)((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)(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;
|
||
|
}
|
||
|
#endif // USE_BINK_SDK
|
||
|
|
||
|
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;
|
||
|
char buff[256];
|
||
|
sprintf(buff, "%s_%d", GetKey()->GetName(), uniqueSuffix++);
|
||
|
|
||
|
plLayer* layer = TRACKED_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 = TRACKED_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;
|
||
|
char buff[256];
|
||
|
sprintf(buff, "%s_%d_d", GetKey()->GetName(), uniqueSuffix++);
|
||
|
|
||
|
plLayer* layer = TRACKED_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 x,y;
|
||
|
x = decalChunk->fAbsoluteX;
|
||
|
y = decalChunk->fAbsoluteY;
|
||
|
|
||
|
if((decalChunk->fFlags & pfEsHTMLChunk::kAlignMask) == pfEsHTMLChunk::kLeft)
|
||
|
x = (UInt16)fPageLMargin;
|
||
|
else if((decalChunk->fFlags & pfEsHTMLChunk::kAlignMask) == pfEsHTMLChunk::kRight)
|
||
|
x = (UInt16)(baseMipmap->GetWidth() - decal->GetWidth());
|
||
|
else if((decalChunk->fFlags & pfEsHTMLChunk::kAlignMask) == pfEsHTMLChunk::kCenter)
|
||
|
x = (UInt16)((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 = TRACKED_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 = TRACKED_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 chunkIdx, const wchar_t *&face, UInt8 &size, UInt8 &flags, hsColorRGBA &color, Int16 &spacing )
|
||
|
{
|
||
|
enum Which
|
||
|
{
|
||
|
kFace = 0x01,
|
||
|
kSize = 0x02,
|
||
|
kFlags = 0x04,
|
||
|
kColor = 0x08,
|
||
|
kSpacing= 0x10,
|
||
|
|
||
|
kAllFound = kFace | kSize | kFlags | kColor | kSpacing
|
||
|
};
|
||
|
|
||
|
// Start empty
|
||
|
UInt8 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 = 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 = L"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 pfJournalBook::IFindLastAlignment( void ) const
|
||
|
{
|
||
|
Int32 idx;
|
||
|
|
||
|
|
||
|
for( idx = fHTMLSource.GetCount() - 1; idx >= 0; idx-- )
|
||
|
{
|
||
|
if( fHTMLSource[ idx ]->fType == pfEsHTMLChunk::kParagraph && fHTMLSource[ idx ]->fFlags != 0 )
|
||
|
return (UInt8)(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 upToPage )
|
||
|
{
|
||
|
UInt32 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 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 type, UInt32 linkID )
|
||
|
{
|
||
|
if( fCallbackKey != nil )
|
||
|
{
|
||
|
plNotifyMsg *pMsg = TRACKED_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( hsScalar width, hsScalar 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( hsBool unload )
|
||
|
{
|
||
|
UInt32 i;
|
||
|
|
||
|
// load the cover
|
||
|
if( fCoverFromHTML && fCoverMipKey != nil )
|
||
|
{
|
||
|
if( unload )
|
||
|
fBookGUIs[fCurBookGUI]->GetKey()->Release( fCoverMipKey );
|
||
|
else
|
||
|
{
|
||
|
plGenRefMsg *ref = TRACKED_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 = TRACKED_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();
|
||
|
}
|
||
|
}
|