#include "cCustomTEDrawing.h" // =========================================================================== // cCustomTEDrawing.cpp Version 1.0 ©1998 Joakim Braun All rights reserved. // =========================================================================== // // CONTENTS: A base class for providing TextEdit custom drawing behavior. // Mainly handles installing and removing of draw hooks. // Subclass to provide actual drawing behavior. // HOW TO USE: Derive a class from cCustomTEDrawing. Override DrawProc() to provide // custom text drawing behavior. Attach the drawing object to a // multistyled TEHandle: // new cCustomTEDrawingDerivedClass(myTEHandle); // TextEdit will cause DrawProc() to be called whenever text needs to be drawn. // When deallocating the TEHandle, don't forget to deallocate your "drawing object", // a pointer to which is stored in the teRefCon field of the style handle: // TEStyleHandle styleH = ::TEGetStyleHandle(myTEHandle); // if(styleH){ // cCustomTEDrawingDerivedClass* theDrawer = (cCustomTEDrawingDerivedClass*)(*styleH)->teRefCon; // if(theDrawer) // delete theDrawer; // } // cCustomTEDrawing is free for any and all use. // Do not distribute modified source code under my name. // No support promised, no liability accepted. Provided "as is". // That said, I can be reached at braun@swipnet.se. // Latest version at http://home4.swipnet.se/~w-41308/ // Change history: // 1.0 September 10, 1998 First release // #define _RESTORE_REGISTERS 1 // Uncomment to have CleanupDrawProc() restore registers unsigned short cCustomTEDrawing::sTextOffset = 0, cCustomTEDrawing::sDrawLen = 0; void* cCustomTEDrawing::sTextBufferPtr = NULL; TEPtr cCustomTEDrawing::sPTE = NULL; TEHandle cCustomTEDrawing::sHTE = NULL; // =========================================================================== // Ä Constructor // =========================================================================== // // NULL member variables. cCustomTEDrawing::cCustomTEDrawing( void){ mTEH = NULL; mOldDrawHook = NULL, mNewDrawHook = NULL; } // =========================================================================== // Ä Constructor // =========================================================================== // // Takes a TEHandle as argument and installs drawing hook cCustomTEDrawing:: cCustomTEDrawing( TEHandle theTEH){ mTEH = theTEH; mOldDrawHook = NULL, mNewDrawHook = NULL; InstallHook(mTEH, this); } // =========================================================================== // Ä Destructor // =========================================================================== // // Replace drawing hook with original hook cCustomTEDrawing::~cCustomTEDrawing( void ){ // Replace hook with old routine if(mOldDrawHook) ::TECustomHook(intDrawHook, (UniversalProcPtr*)&mOldDrawHook, mTEH); if(mNewDrawHook) DisposeRoutineDescriptor(mNewDrawHook); } // =========================================================================== // Ä InstallHook() // =========================================================================== // // Static function installs drawing hook in TEHandle. // TEHandle must be multistyled (created with ::TEStyleNew()). // Stores pointer to drawing object in teRefCon field of TEStyleHandle, so don't mess with it. void cCustomTEDrawing:: InstallHook(TEHandle theTEH, cCustomTEDrawing* theDrawer){ TEStyleHandle styleH = ::TEGetStyleHandle(theTEH); // We're only doing multistyled TextEdit // (No place to put a ptr to ourselves in monostyled TextEdit) if(!styleH) SignalPStr_("\pNo TEStyleHandle!"); // Store pointer to ourselves in TEStyleHandle (*styleH)->teRefCon = (long)theDrawer; // Install static drawing hook theDrawer->mTEH = theTEH; theDrawer->mNewDrawHook = NewDrawHookProc(StaticTEDrawProc); theDrawer->mOldDrawHook = theDrawer->mNewDrawHook; // TECustomHook() will put the UPP we're replacing into mOldDrawHook ::TECustomHook(intDrawHook, (UniversalProcPtr*)&theDrawer->mOldDrawHook, theTEH); } // =========================================================================== // Ä StaticTEDrawProc() // =========================================================================== // // Called by TextEdit whenever text needs to be drawn. // Override PrepareDrawProc() and CleanupDrawProc() to provide custom drawing-related initialization/cleanup. pascal void cCustomTEDrawing::StaticTEDrawProc(void){ PrepareDrawProc(); TEStyleHandle styleH = ::TEGetStyleHandle(sHTE); if(!styleH) SignalPStr_("\pNo TEStyleHandle!"); else{ // Get pointer to object from style handle, // let's hope no one mucked with the teRefCon field cCustomTEDrawing* theDrawer = (cCustomTEDrawing*)(*styleH)->teRefCon; if(theDrawer) theDrawer->DrawProc(); } CleanupDrawProc(); } // =========================================================================== // Ä DrawProc() // =========================================================================== // // The actual drawing routine called by StaticTEDrawProc(). Override this to provide custom drawing. // When this is called, the static variables are setup according to the documentation // on custom TextEdit draw hooks in Inside Mac: Text: sTextBufferPtr points to a buffer of characters, // sTextOffset is the offset from start of sTextBufferPtr of the text to draw, sDrawLen is the number // of characters to draw, sPTE is a pointer to the TEHandle and sHTE is the TEHandle itself. // In the base class, all we'll do is call CallDefaultDrawProc(). void cCustomTEDrawing::DrawProc(void){ CallDefaultDrawProc(); } // =========================================================================== // Ä CallDefaultDrawProc() // =========================================================================== // // We really should call our mOldDrawHook saved UPP here. Anyone know how to do it safely on both 68K and PPC? // Until we know, call ::DrawText(), which according to Inside Mac/Text is what's stored in the default UPP. void cCustomTEDrawing::CallDefaultDrawProc(void){ ::DrawText(sTextBufferPtr, sTextOffset, sDrawLen); } // =========================================================================== // Ä PrepareDrawProc() // =========================================================================== // // Move parameters from registers into static variables asm pascal void cCustomTEDrawing::PrepareDrawProc(void){ move.w D0, sTextOffset move.w D1, sDrawLen move.l A0, sTextBufferPtr move.l A3, sPTE move.l A4, sHTE preturn } // =========================================================================== // Ä CleanupDrawProc() // =========================================================================== // // Restore registers (I don't pretend to know what I'm doing here) asm pascal void cCustomTEDrawing::CleanupDrawProc(void){ #ifdef _RESTORE_REGISTERS move.w sTextOffset, D0 move.w sDrawLen, D1 move.l sTextBufferPtr, A0 move.l sPTE, A3 move.l sHTE, A4 #endif preturn } // =========================================================================== // Ä CalcLineFromOffset() // =========================================================================== // // Given a correct offset into the text, compute which line we're in. Uint16 cCustomTEDrawing::CalcLineFromOffset(TEHandle theTEH, Uint16 offset){ Uint16 result = 0; for(; result < (*theTEH)->nLines; result++){ if((*theTEH)->lineStarts[result + 1] > offset) break; } if(result > (*theTEH)->nLines) SignalPStr_("\pMucked in cCustomTEDrawing::CalcLineFromOffset()!"); return result; } // =========================================================================== // Ä GetRealOffset() // =========================================================================== // // TextEdit does not pass the draw proc a base dereference to the hText handle and then an offset from zero, // but a pointer to the first character of the text to be drawn draw and an offset of zero. // This routine compares the text pointer with the address of the first byte of the TEHandle hText, // and returns the actual offset from the first character of the TEHandle hText field. Uint16 cCustomTEDrawing::GetRealOffset(TEHandle theTEH, Uint16 fakeOffset){ CharsHandle charsH = ::TEGetText(theTEH); char* charPtr = &**charsH; Uint16 result = &((char*)sTextBufferPtr)[fakeOffset] - charPtr; if(result > (*theTEH)->teLength) SignalPStr_("\pMucked in cCustomTEDrawing::GetRealOffset()!"); return result; }