/// Include
#include <exec/libraries.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <libraries/mui.h>
#include <libraries/iffparse.h>
#include <iffp/ftxt.h>

#ifdef TEXTDISPLAY_TEST
#include <dos/dostags.h>
#include <workbench/workbench.h>
#include <mui/textinput_mcc.h>
#include <libraries/asl.h>
#endif

#include <clib/muimaster_protos.h>
#include <clib/alib_protos.h>
#include <pragmas/muimaster_pragmas.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <proto/graphics.h>
#include <proto/iffparse.h>

#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "TextDisplay.h"
///
/// Prototypes
#define NEWSTACK_SIZE   (1024 * 16)

#define QUAL_SHIFT      (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
#define QUAL_ALT        (IEQUALIFIER_LALT | IEQUALIFIER_RALT)
#define QUAL_CTRL       (IEQUALIFIER_CONTROL)

#define TD_MARGINS      20
#define TD_YMARGINS     2

static ULONG    DoSuperNew( struct IClass *, Object *, ULONG, ... );
static LONG     xget( Object *, ULONG );
static APTR     GetNode( struct MinList *, ULONG );
static void     notify( struct IClass *, APTR, ... );

#ifdef TEXTDISPLAY_TEST
struct MUIP_MainWindow_AppMessage   { ULONG MethodID; struct AppMessage *AppMsg; };
struct MUIP_MainWindow_Open         { ULONG MethodID; STRPTR FileName; };
struct MUIP_MainWindow_Search       { ULONG MethodID; LONG Offset; LONG Step; };

int main( int, char ** );
void RealMain( int argc, char *argv[] );
void CleanUp( int RetCode );

static Object  *MakeToggleButton( STRPTR );

ULONG SAVEDS ASM MainWindow_Dispatcher( REG( a0 ) struct IClass *, REG( a2 ) Object *, REG( a1 ) Msg );

static ULONG        MainWindow_New( struct IClass *, Object *, struct opSet * );
static ULONG        MainWindow_Open( struct IClass *, Object *, struct MUIP_MainWindow_Open * );
static ULONG        MainWindow_Search( struct IClass *, Object *, struct MUIP_MainWindow_Search * );
static ULONG        MainWindow_Print( struct IClass *, Object *, Msg );
static ULONG        MainWindow_AppMessage( struct IClass *, Object *, struct MUIP_MainWindow_AppMessage * );
static ULONG        MainWindow_Goto( struct IClass *, Object *, Msg );

static void SAVEDS  PrintChild( void );

#define MUIM_MainWindow_Print       (TAGBASE + 1)
#define MUIM_MainWindow_AppMessage  (TAGBASE + 2)
#define MUIM_MainWindow_Open        (TAGBASE + 3)
#define MUIM_MainWindow_Search      (TAGBASE + 4)
#define MUIM_MainWindow_Goto        (TAGBASE + 5)

struct MainWindowData {
        Object *Search;
        Object *File;
        Object *Pop;
        Object *Goto;
        LONG    SearchLine;
};
#endif

static ULONG    TextDisplayScroll_New( struct IClass *, Object *, struct opSet * );
static ULONG    TextDisplayScroll_Notify( struct IClass *, Object *, Msg );

static ULONG    TextDisplay_New( struct IClass *, Object *, struct opSet * );
static ULONG    TextDisplay_Dispose( struct IClass *, Object *, Msg );
static ULONG    TextDisplay_Setup( struct IClass *, Object *, Msg  );
static ULONG    TextDisplay_Cleanup( struct IClass *, Object *, Msg );
static ULONG    TextDisplay_AskMinMax( struct IClass *, Object *, struct MUIP_AskMinMax * );
static ULONG    TextDisplay_Draw( struct IClass *, Object *, struct MUIP_Draw * );
static ULONG    TextDisplay_Show( struct IClass *, Object *, struct MUIP_Show * );
static ULONG    TextDisplay_Set( struct IClass *, Object *, struct opSet * );
static ULONG    TextDisplay_Get( struct IClass *, Object *, struct opGet * );
static ULONG    TextDisplay_HandleInput( struct IClass *, Object *, struct MUIP_HandleInput * );
static ULONG    TextDisplay_Scroll( struct IClass *, Object *, struct MUIP_TextDisplay_Scroll * );
static ULONG    TextDisplay_LoadFile( struct IClass *, Object *, struct MUIP_TextDisplay_LoadFile * );
static ULONG    TextDisplay_Flush( struct IClass *, Object *, Msg );
static ULONG    TextDisplay_CopyToClip( struct IClass *, Object *, Msg );
static ULONG    TextDisplay_SetMark( struct IClass *, Object *, struct MUIP_TextDisplay_SetMark * );
static ULONG    TextDisplay_RedrawLine( struct IClass *, Object *, struct MUIP_TextDisplay_RedrawLine * );

static LONG     GetRealSize( STRPTR, ULONG, ULONG, ULONG );
static ULONG    GetPrintSize( STRPTR, ULONG, ULONG, ULONG );
static LONG     GetLine( STRPTR, ULONG, struct TextFont *, struct TextDisplayData * );
static void     RastWrite( UBYTE *, ULONG, struct RastPort *, struct TextFont *, struct TextDisplayData * );
static UBYTE   *DoCSI( UBYTE *, struct RastPort *, struct TextDisplayData * );
static UBYTE   *DoCtrl( UBYTE *, struct RastPort *, struct TextFont *, struct TextDisplayData * );

static void     WordWrap( Object *, ULONG, struct TextDisplayData * );
///
/// Data
struct MUI_CustomClass *CL_TextDisplay;
struct MUI_CustomClass *CL_TextDisplayScroll;

#ifdef TEXTDISPLAY_TEST
struct Library *MUIMasterBase;
struct Library *UtilityBase;

ULONG           Childs;
Object         *Display;

struct Task    *ThisTask;

struct MUI_CustomClass *CL_MainWindow;

static struct StackSwapStruct   StackSwapper;
static BOOL                     StackSwapped;
#endif
///

/// DoSuperNew
static ULONG DoSuperNew( struct IClass *cl, Object *obj, ULONG tag1, ... )
{
    return( DoSuperMethod( cl, obj, OM_NEW, &tag1, NULL ));
}
///
/// xget
static LONG xget( Object *obj, ULONG attribute )
{
    LONG    x;

    get( obj, attribute, &x );

    return( x );
}
///
/// notify
static void notify( struct IClass *notifyclass, APTR obj, ... )
{
    CoerceMethod( notifyclass, obj, OM_SET, &obj + 1 );
}
///
#ifdef TEXTDISPLAY_TEST
/// MakeToggleButton
static Object *MakeToggleButton( STRPTR str )
{
    Object *obj = MUI_MakeObject( MUIO_Button, str );

    if( obj ) {

        SetAttrs( obj,
                  MUIA_CycleChain, TRUE,
                  MUIA_InputMode,  MUIV_InputMode_Toggle,
                  TAG_DONE );
    }

    return( obj );
}
///
#endif


/// GetNode
static APTR GetNode( struct MinList *List, ULONG Number )
{
    struct MinNode *Node;
    ULONG           i;

    Node = List->mlh_Head;

    for( i = 0; Node && ( i < Number ); i++ )
        Node = Node->mln_Succ;

    return( Node );
}
///


#ifdef TEXTDISPLAY_TEST
/// main
#ifdef WBMAIN
void wbmain( struct WBStartup *msg )
{
    struct WBArg   *args;
    TEXT            prg[256], file[256];
    char           *mainargs[] = { prg, file };
    ULONG           num = 1;

    args = msg->sm_ArgList;

    NameFromLock( args[0].wa_Lock, prg, sizeof( prg ));

    AddPart( prg, args[0].wa_Name, sizeof( prg ));

    if( msg->sm_NumArgs > 1 ) {

        NameFromLock( args[1].wa_Lock, file, sizeof( file ));

        AddPart( file, args[1].wa_Name, sizeof( file ));

        num = 2;
    }

    main( num, mainargs );
}
#endif

int main( int argc, char *argv[] )
{
    ThisTask = FindTask( NULL );

    if(((ULONG)ThisTask->tc_SPUpper - (ULONG)ThisTask->tc_SPLower ) < NEWSTACK_SIZE ) {

        if(!( StackSwapper.stk_Lower = AllocMem( NEWSTACK_SIZE, 0L )))
            return( 20 );

        StackSwapper.stk_Upper   = (ULONG)StackSwapper.stk_Lower + NEWSTACK_SIZE;
        StackSwapper.stk_Pointer = (APTR)StackSwapper.stk_Upper;

        StackSwap( &StackSwapper );

        StackSwapped = TRUE;
    }

    RealMain( argc, argv );
}

void RealMain( int argc, char *argv[] )
{
    Object *AppObj, *MainWin;

    UtilityBase = OpenLibrary( "utility.library", 0 );

    if(!( MUIMasterBase = OpenLibrary( "muimaster.library", 19 )))
        CleanUp( 20 );

    if(!( CL_TextDisplay = MUI_CreateCustomClass( NULL, MUIC_Area,
                                                  NULL, sizeof( struct TextDisplayData ),
                                                  TextDisplay_Dispatcher )))
        CleanUp( 20 );

    if(!( CL_TextDisplayScroll = MUI_CreateCustomClass( NULL, MUIC_Group,
                                                        NULL, sizeof( struct TextDisplayScrollData ),
                                                        TextDisplayScroll_Dispatcher )))
        CleanUp( 20 );

    if(!( CL_MainWindow = MUI_CreateCustomClass( NULL, MUIC_Window,
                                                 NULL, sizeof( struct MainWindowData ),
                                                 MainWindow_Dispatcher )))
        CleanUp( 20 );

    if( AppObj = ApplicationObject,

                        MUIA_Application_Title,         "TextDisplay",
                        MUIA_Application_Version,       "$VER: TextDisplay 1.3 (20.9.99) ©Simone Tellini",
                        MUIA_Application_Copyright,     "Copyright ©1998-1999 by Simone Tellini",
                        MUIA_Application_Author,        "Simone Tellini <wiz@vapor.com>",
                        MUIA_Application_Description,   "Fast text viewer",
                        MUIA_Application_Base,          "TEXTDISPLAY",

                        SubWindow, MainWin = NewObject( CL_MainWindow->mcc_Class, 0,
                        End,
                   End ) {
        ULONG   sigs = 0;

        DoMethod( AppObj, MUIM_Application_Load, MUIV_Application_Load_ENV );

        DoMethod( MainWin, MUIM_MainWindow_Open, ( argc >= 2 ) ? argv[1] : NULL );

        while( DoMethod( AppObj, MUIM_Application_NewInput, &sigs ) != MUIV_Application_ReturnID_Quit ) {
            if( sigs ) {

                sigs = Wait( sigs | SIGBREAKF_CTRL_C );

                if( sigs & SIGBREAKF_CTRL_C )
                    break;
            }
        }

        DoMethod( AppObj, MUIM_Application_Save, MUIV_Application_Save_ENV    );
        DoMethod( AppObj, MUIM_Application_Save, MUIV_Application_Save_ENVARC );

        MUI_DisposeObject( AppObj );
    }

    CleanUp( 0 );
}
///
/// CleanUp
void CleanUp( int RetCode )
{
    if( CL_MainWindow        )  MUI_DeleteCustomClass( CL_MainWindow        );
    if( CL_TextDisplay       )  MUI_DeleteCustomClass( CL_TextDisplay       );
    if( CL_TextDisplayScroll )  MUI_DeleteCustomClass( CL_TextDisplayScroll );

    if( StackSwapped ) {
        StackSwap( &StackSwapper );
        FreeMem( StackSwapper.stk_Lower, NEWSTACK_SIZE );
    }

    while( Childs )
        Delay( 25 );

    if( MUIMasterBase )
        CloseLibrary( MUIMasterBase );

    if( UtilityBase )
        CloseLibrary( UtilityBase );

    exit( RetCode );
}
///

/// MainWindow_Dispatcher
ULONG SAVEDS ASM MainWindow_Dispatcher( REG( a0 ) struct IClass *cl, REG( a2 ) Object *obj, REG( a1 ) Msg msg )
{
    switch( msg->MethodID ) {

        case OM_NEW:
            return( MainWindow_New( cl, obj, (APTR)msg ));

        case MUIM_MainWindow_Open:
            return( MainWindow_Open( cl, obj, (APTR)msg ));

        case MUIM_MainWindow_Search:
            return( MainWindow_Search( cl, obj, (APTR)msg ));

        case MUIM_MainWindow_Print:
            return( MainWindow_Print( cl, obj, (APTR)msg ));

        case MUIM_MainWindow_AppMessage:
            return( MainWindow_AppMessage( cl, obj, (APTR)msg ));

        case MUIM_MainWindow_Goto:
            return( MainWindow_Goto( cl, obj, (APTR)msg ));
    }

    return( DoSuperMethodA( cl, obj, msg ));
}
///
/// MainWindow_New
static ULONG MainWindow_New( struct IClass *cl, Object *obj, struct opSet *msg )
{
    struct MainWindowData   temp = { 0 };
    Object                 *fg, *back, *mark, *tab;
    Object                 *clip, *hide, *print, *copy;

    if( obj = ( Object * )DoSuperNew( cl, obj,

                            MUIA_Window_ID,                     MAKE_ID( 'M', 'A', 'I', 'N' ),
                            MUIA_Window_Title,                  "TextDisplay - ©1998-1999 Simone Tellini",
                            MUIA_Window_AppWindow,              TRUE,
                            MUIA_Window_UseRightBorderScroller, TRUE,

                            WindowContents, VGroup,

                                Child, HGroup,

                                    Child, print = MUI_MakeObject( MUIO_Button, "_Print" ),
                                    Child, MUI_MakeObject( MUIO_Label, "Search for:", MUIO_Label_SingleFrame ),
                                    Child, temp.Search = TextinputObject,
                                                        StringFrame,
                                                        MUIA_CycleChain,            1,
                                                        MUIA_Textinput_AdvanceOnCR, FALSE,
                                                        MUIA_Textinput_MaxLen,      256,
                                                    End,

                                    Child, temp.Pop = PopaslObject,
                                                MUIA_Weight,            350,
                                                MUIA_Popstring_String,  temp.File = TextinputObject,
                                                                            StringFrame,
                                                                            MUIA_CycleChain,            1,
                                                                            MUIA_Textinput_AdvanceOnCR, FALSE,
                                                                            MUIA_Textinput_MaxLen,      256,
                                                                        End,
                                                MUIA_Popstring_Button,  PopButton( MUII_PopFile ),
                                                MUIA_Popasl_Type,       ASL_FileRequest,
                                            End,
                                End,

                                Child, Display = TextDisplayObject,
                                            MUIA_TextDisplay_UseWinBorder, TRUE,
                                       End,

                                Child, HGroup,
                                    Child, MUI_MakeObject( MUIO_Label, "Go to line:", MUIO_Label_SingleFrame ),
                                    Child, temp.Goto = TextinputObject,
                                                    StringFrame,
                                                    MUIA_CycleChain,            1,
                                                    MUIA_Weight,                40,
                                                    MUIA_Textinput_AdvanceOnCR, FALSE,
                                                 End,

                                    Child, tab = SliderObject,
                                                    MUIA_Weight,        40,
                                                    MUIA_Numeric_Min,   1,
                                                    MUIA_Numeric_Max,   8,
                                                    MUIA_Numeric_Value, 8,
                                                    MUIA_ObjectID,      MAKE_ID( 'T', 'A', 'B', 'S' ),
                                                 End,

                                    Child, HGroup,
                                        MUIA_Weight,        30,

                                        Child, copy = MUI_MakeObject( MUIO_Button, "_Copy" ),

                                        Child, HGroup,

                                            MUIA_Group_Spacing, 0,

                                            Child, clip = MakeToggleButton( "Auto Copy" ),
                                            Child, hide = MakeToggleButton( "Auto Hide" ),
                                        End,
                                    End,

                                    Child, HGroup,

                                        MUIA_Weight,        20,
                                        MUIA_Group_Spacing, 0,

                                        Child, fg = PoppenObject,
                                                        MUIA_CycleChain,    1,
                                                        MUIA_Window_Title,  "Text color",
                                                        MUIA_ObjectID,      MAKE_ID( 't', 'e', 'x', 't' ),
                                                    End,

                                        Child, back = PoppenObject,
                                                        MUIA_CycleChain,    1,
                                                        MUIA_Window_Title,  "Background color",
                                                        MUIA_ObjectID,      MAKE_ID( 'b', 'a', 'c', 'k' ),
                                                    End,

                                        Child, mark = PoppenObject,
                                                        MUIA_CycleChain,    1,
                                                        MUIA_Window_Title,  "Marker color",
                                                        MUIA_ObjectID,      MAKE_ID( 'm', 'a', 'r', 'k' ),
                                                    End,
                                    End,
                                End,
                            End,

                        TAG_MORE, msg->ops_AttrList )) {

        struct MainWindowData  *data = INST_DATA( cl, obj );

        *data = temp;

        set( fg,   MUIA_Pendisplay_Spec, xget( Display, MUIA_TextDisplay_TextPen ));
        set( back, MUIA_Pendisplay_Spec, xget( Display, MUIA_TextDisplay_BackPen ));
        set( mark, MUIA_Pendisplay_Spec, xget( Display, MUIA_TextDisplay_MarkPen ));

        DoMethod( obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, MUIV_Notify_Application, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit );
        DoMethod( obj, MUIM_Notify, MUIA_AppMessage, MUIV_EveryTime, obj, 2, MUIM_MainWindow_AppMessage, MUIV_TriggerValue );

        DoMethod( Display, MUIM_Notify, MUIA_TextDisplay_FirstLine, MUIV_EveryTime, temp.Goto, 3, MUIM_Set, MUIA_String_Integer, MUIV_TriggerValue );
        DoMethod( temp.File, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, Display, 2, MUIM_TextDisplay_LoadFile, MUIV_TriggerValue );
        DoMethod( temp.Goto, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, obj, 1, MUIM_MainWindow_Goto );

        DoMethod( tab, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime, Display, 3, MUIM_Set, MUIA_TextDisplay_TabSize, MUIV_TriggerValue );

        DoMethod( copy, MUIM_Notify, MUIA_Pressed, FALSE, Display, 1, MUIM_TextDisplay_CopyToClip );

        DoMethod( clip, MUIM_Notify, MUIA_Selected, MUIV_EveryTime, Display, 3, MUIM_Set, MUIA_TextDisplay_AutoCopy, MUIV_TriggerValue );
        DoMethod( clip, MUIM_Notify, MUIA_Selected, MUIV_EveryTime, copy, 3, MUIM_Set, MUIA_Disabled, MUIV_TriggerValue );
        DoMethod( hide, MUIM_Notify, MUIA_Selected, MUIV_EveryTime, Display, 3, MUIM_Set, MUIA_TextDisplay_HideMark, MUIV_TriggerValue );

        DoMethod( fg,   MUIM_Notify, MUIA_Pendisplay_Spec, MUIV_EveryTime, Display, 3, MUIM_Set, MUIA_TextDisplay_TextPen, MUIV_TriggerValue );
        DoMethod( back, MUIM_Notify, MUIA_Pendisplay_Spec, MUIV_EveryTime, Display, 3, MUIM_Set, MUIA_TextDisplay_BackPen, MUIV_TriggerValue );
        DoMethod( mark, MUIM_Notify, MUIA_Pendisplay_Spec, MUIV_EveryTime, Display, 3, MUIM_Set, MUIA_TextDisplay_MarkPen, MUIV_TriggerValue );

        DoMethod( print,  MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, MUIM_MainWindow_Print );
        DoMethod( temp.Search, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, obj, 3, MUIM_MainWindow_Search, 0, 1 );
        DoMethod( temp.Search, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, obj, 3, MUIM_WriteLong, -1, &data->SearchLine );

        DoMethod( obj, MUIM_Notify, MUIA_Window_InputEvent, "F1", obj, 3, MUIM_MainWindow_Search, 1, 1 );
        DoMethod( obj, MUIM_Notify, MUIA_Window_InputEvent, "F2", obj, 3, MUIM_MainWindow_Search, -1, -1 );

        DoMethod( obj, MUIM_Notify, MUIA_Window_InputEvent, "shift F1", obj, 3, MUIM_Set, MUIA_Window_ActiveObject, temp.Search );

        set( clip, MUIA_Selected, TRUE );
        set( hide, MUIA_Selected, TRUE );

        data->SearchLine = -1;

        return(( ULONG )obj );
    }

    return( 0 );
}
///
/// MainWindow_Open
static ULONG MainWindow_Open( struct IClass *cl, Object *obj, struct MUIP_MainWindow_Open *msg )
{
    struct MainWindowData  *data = INST_DATA( cl, obj );

    set( obj, MUIA_Window_Open, TRUE );

    if( msg->FileName ) {

        set( data->File, MUIA_String_Contents, msg->FileName );

        DoMethod( Display, MUIM_TextDisplay_LoadFile, msg->FileName );

    } else
        DoMethod( data->Pop, MUIM_Popstring_Open );

    return( 0 );
}
///
/// MainWindow_Search
static ULONG MainWindow_Search( struct IClass *cl, Object *obj, struct MUIP_MainWindow_Search *msg )
{
    struct MainWindowData  *data = INST_DATA( cl, obj );
    STRPTR                  search;

    get( data->Search, MUIA_String_Contents, &search );

    if( search[0] ) {
        struct MinList *lines;

        get( Display, MUIA_TextDisplay_Lines, &lines );

        if( lines ) {
            ULONG               num;
            LONG                first, in_chunk;
            struct LinesChunk  *chunk;
            TEXT                to_find[256];

            for( first = 0; search[ first ]; first++ )
                to_find[ first ] = tolower( search[ first ]);

            to_find[ first ] = '\0';

            if( data->SearchLine == -1 )
                get( Display, MUIA_TextDisplay_FirstLine, &first );
            else
                first = data->SearchLine;

            get( Display, MUIA_TextDisplay_NumLines, &num );

            first   += msg->Offset;

            chunk    = GetNode( lines, first / CHUNK_SIZE );
            in_chunk = first % CHUNK_SIZE;

            while(( first >= 0 ) && ( first < num )) {
                TEXT    buffer[1024];
                STRPTR  from, to;
                ULONG   i;

                from = chunk->Lines[ in_chunk ].Line;
                to   = buffer;

                for( i = 0; i < chunk->Lines[ in_chunk ].Length; i++ )
                {
                    *to = tolower( *from );
                    to++; from++;
                }

                *to = '\0';

                if( from = strstr( buffer, to_find )) {
                    ULONG   start, end, tab;

                    get( Display, MUIA_TextDisplay_TabSize, &tab );

                    to    = chunk->Lines[ in_chunk ].Line;
                    start = GetPrintSize( to, from - buffer, tab, 0 );
                    end   = start + GetPrintSize( from, strlen( to_find ), tab, (ULONG)from - start ) - 1;

                    DoMethod( Display, MUIM_TextDisplay_SetMark, first, first, start, end );

                    set( Display, MUIA_TextDisplay_FirstLine, first );

                    if( data->SearchLine != -1 )
                        DoMethod( Display, MUIM_TextDisplay_RedrawLine, data->SearchLine );

                    DoMethod( Display, MUIM_TextDisplay_RedrawLine, first );

                    data->SearchLine = first;

                    return( 0 );
                }

                first    += msg->Step;
                in_chunk += msg->Step;

                if( in_chunk >= CHUNK_SIZE ) {

                    in_chunk = 0;
                    chunk    = chunk->Succ;

                } else if( in_chunk < 0 ) {

                    in_chunk = CHUNK_SIZE - 1;
                    chunk    = chunk->Pred;
                }
            }

            DisplayBeep( NULL );
        }
    }

    return( 0 );
}
///
/// MainWindow_Print
static ULONG MainWindow_Print( struct IClass *cl, Object *obj, Msg msg )
{
    if( xget( Display, MUIA_TextDisplay_Contents )) {

        CreateNewProcTags( NP_Entry,     PrintChild,
                           NP_Priority,  -1,
                           NP_Name,      "TextDisplay Print Child",
                           TAG_END );

        Wait( SIGBREAKF_CTRL_F );
    }

    return( 0 );
}
///
/// MainWindow_AppMessage
static ULONG MainWindow_AppMessage( struct IClass *cl, Object *obj, struct MUIP_MainWindow_AppMessage *Msg )
{
    struct AppMessage      *msg;
    ULONG                   i;
    BPTR                    old;

    msg = Msg->AppMsg;

    old = CurrentDir( msg->am_ArgList->wa_Lock );

    for( i = 0; i < msg->am_NumArgs; i++ ) {
        BPTR    lock;

        CurrentDir( msg->am_ArgList[ i ].wa_Lock );

        if( lock = Lock( msg->am_ArgList[ i ].wa_Name, ACCESS_READ )) {
            TEXT    file[256];

            NameFromLock( lock, file, sizeof( file ));

            DoMethod( Display, MUIM_TextDisplay_LoadFile, file );

            UnLock( lock );

            break;
        }
    }

    CurrentDir( old );

    return( 0 );
}
///
/// MainWindow_Goto
static ULONG MainWindow_Goto( struct IClass *cl, Object *obj, Msg msg )
{
    struct MainWindowData  *data = INST_DATA( cl, obj );

    if( xget( Display, MUIA_TextDisplay_Contents ))
        set( Display, MUIA_TextDisplay_FirstLine, xget( data->Goto, MUIA_String_Integer ));

    return( 0 );
}
///

/// PrintChild
static void SAVEDS PrintChild( void )
{
    STRPTR  from, buffer;
    ULONG   len;

    Childs += 1;

    get( Display, MUIA_TextDisplay_Contents, &from );

    len = strlen( from );

    if( buffer = AllocMem( len, MEMF_ANY )) {
        BPTR    prt;

        CopyMem( from, buffer, len );

        Signal( ThisTask, SIGBREAKF_CTRL_F );

        if( prt = Open( "PRT:", MODE_OLDFILE )) {

            Write( prt, buffer, len );

            Close( prt );
        }

        FreeMem( buffer, len );

    } else
        Signal( ThisTask, SIGBREAKF_CTRL_F );

    Childs -= 1;
}
///
#endif

/// TextDisplayScroll_Dispatcher
ULONG SAVEDS ASM TextDisplayScroll_Dispatcher( REG( a0 ) struct IClass *cl, REG( a2 ) Object *obj, REG( a1 ) Msg msg )
{
    switch( msg->MethodID ) {

        case OM_NEW:
            return( TextDisplayScroll_New( cl, obj, (APTR)msg ));

        case MUIM_Notify:
            return( TextDisplayScroll_Notify( cl, obj, (APTR)msg ));
    }

    return( DoSuperMethodA( cl, obj, msg ));
}
///
/// TextDisplayScroll_New
static ULONG TextDisplayScroll_New( struct IClass *cl, Object *obj, struct opSet *msg )
{
    Object *disp, *scroll;
    ULONG   usewinborder = GetTagData( MUIA_TextDisplay_UseWinBorder, FALSE, msg->ops_AttrList ) ? MUIA_Prop_UseWinBorder : TAG_IGNORE;

    if( obj = ( Object * )DoSuperNew( cl, obj,

                            MUIA_Group_Horiz,   TRUE,
                            MUIA_Group_Spacing, 0,

                            Child, disp   = NewObject( CL_TextDisplay->mcc_Class, 0, TAG_DONE ),
                            Child, scroll = ScrollbarObject,
                                                usewinborder, MUIV_Prop_UseWinBorder_Right,
                                            End,

                        TAG_MORE, msg->ops_AttrList )) {

        struct TextDisplayScrollData   *data = INST_DATA( cl, obj );

        data->Display = disp;

        set( disp, MUIA_TextDisplay_Scroller, scroll );

        DoMethod( scroll, MUIM_Notify, MUIA_Prop_First, MUIV_EveryTime,
                  disp, 2, MUIM_TextDisplay_Scroll, MUIV_TriggerValue );

        return(( ULONG )obj );
    }

    return( 0 );
}
///
/// TextDisplayScroll_Notify
static ULONG TextDisplayScroll_Notify( struct IClass *cl, Object *obj, Msg msg )
{
    struct TextDisplayScrollData   *data = INST_DATA( cl, obj );

    return( DoMethodA( data->Display, msg ));
}
///

/// TextDisplay_Dispatcher
ULONG SAVEDS ASM TextDisplay_Dispatcher( REG( a0 ) struct IClass *cl, REG( a2 ) Object *obj, REG( a1 ) Msg msg )
{
    switch( msg->MethodID ) {

        case OM_NEW:
            return( TextDisplay_New( cl, obj, (APTR)msg ));

        case OM_DISPOSE:
            return( TextDisplay_Dispose( cl, obj, (APTR)msg ));

        case OM_SET:
            return( TextDisplay_Set( cl, obj, (APTR)msg ));

        case OM_GET:
            return( TextDisplay_Get( cl, obj, (APTR)msg ));

        case MUIM_Setup:
            return( TextDisplay_Setup( cl, obj, msg ));

        case MUIM_Cleanup:
            return( TextDisplay_Cleanup( cl, obj, msg ));

        case MUIM_AskMinMax:
            return( TextDisplay_AskMinMax( cl, obj, (APTR)msg ));

        case MUIM_HandleInput:
            return( TextDisplay_HandleInput( cl, obj, (APTR)msg ));

        case MUIM_Show:
            return( TextDisplay_Show( cl, obj, (APTR)msg ));

        case MUIM_Draw:
            return( TextDisplay_Draw( cl, obj, (APTR)msg ));

        case MUIM_TextDisplay_Scroll:
            return( TextDisplay_Scroll( cl, obj, (APTR)msg ));

        case MUIM_TextDisplay_LoadFile:
            return( TextDisplay_LoadFile( cl, obj, (APTR)msg ));

        case MUIM_TextDisplay_Flush:
            return( TextDisplay_Flush( cl, obj, (APTR)msg ));

        case MUIM_TextDisplay_CopyToClip:
            return( TextDisplay_CopyToClip( cl, obj, (APTR)msg ));

        case MUIM_TextDisplay_SetMark:
            return( TextDisplay_SetMark( cl, obj, (APTR)msg ));

        case MUIM_TextDisplay_RedrawLine:
            return( TextDisplay_RedrawLine( cl, obj, (APTR)msg ));
    }

    return( DoSuperMethodA( cl, obj, msg ));
}
///
/// TextDisplay_New
static ULONG TextDisplay_New( struct IClass *cl, Object *obj, struct opSet *msg )
{
    APTR    pool;

    if(!( pool = LibCreatePool( MEMF_ANY, sizeof( struct LinesChunk ) * 20, sizeof( struct LinesChunk ) * 20 )))
        return( 0 );

    if( obj = ( Object * )DoSuperNew( cl, obj,

                            MUIA_Background,    MUII_TextBack,
                            MUIA_FillArea,      FALSE,
                            MUIA_Frame,         MUIV_Frame_Text,
                            MUIA_Font,          MUIV_Font_Fixed,

                        TAG_MORE, msg->ops_AttrList )) {

        struct TextDisplayData *data = INST_DATA( cl, obj );

        memset( data, 0, sizeof( *data ));

        data->Pool = pool;

        NewList(( struct List * )&data->Lines );

        data->TabSize     = GetTagData( MUIA_TextDisplay_TabSize, 8, msg->ops_AttrList );

        data->TextPenSpec = (APTR)GetTagData( MUIA_TextDisplay_TextPen, (ULONG)"r00000000,00000000,00000000", msg->ops_AttrList );
        data->BackPenSpec = (APTR)GetTagData( MUIA_TextDisplay_BackPen, (ULONG)"rFFFFFFFF,FFFFFFFF,FFFFFFFF", msg->ops_AttrList );
        data->MarkPenSpec = (APTR)GetTagData( MUIA_TextDisplay_MarkPen, (ULONG)"r66666666,66666666,88888888", msg->ops_AttrList );

        data->Clipboard = GetTagData( MUIA_TextDisplay_ClipUnit, 0, msg->ops_AttrList );

        if( GetTagData( MUIA_TextDisplay_AutoCopy, TRUE, msg->ops_AttrList ))
            data->Flags |= TD_AUTOCOPY;

        if( GetTagData( MUIA_TextDisplay_HideMark, TRUE, msg->ops_AttrList ))
            data->Flags |= TD_HIDEMARK;

        data->Notify = MUI_GetClass( MUIC_Notify );

        return(( ULONG )obj );
    }

    LibDeletePool( pool );

    return( 0 );
}
///
/// TextDisplay_Dispose
static ULONG TextDisplay_Dispose( struct IClass *cl, Object *obj, Msg msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    data->Flags |= TD_DISPOSING;

    DoMethod( obj, MUIM_TextDisplay_Flush );

    LibDeletePool( data->Pool );

    return( DoSuperMethodA( cl, obj, (Msg) msg ));
}
///
/// TextDisplay_Set
static ULONG TextDisplay_Set( struct IClass *cl, Object *obj, struct opSet *msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );
    struct TagItem         *tags, *tag;


    for( tags = msg->ops_AttrList; tag = NextTagItem( &tags ); ) {

        switch( tag->ti_Tag ) {

            case MUIA_TextDisplay_Scroller:
                data->Scroller = (Object *)tag->ti_Data;
                break;

            case MUIA_TextDisplay_FirstLine: {
                ULONG   newfirst;

                newfirst = MIN( tag->ti_Data, data->NumLines );

                if( data->NumLines <= data->DisplayedLines )
                    newfirst = 0;

                else if( newfirst > data->NumLines - data->DisplayedLines )
                    newfirst = data->NumLines - data->DisplayedLines;

                if( data->First != newfirst ) {
                    data->First = newfirst;
                    set( data->Scroller, MUIA_Prop_First, newfirst );
                    MUI_Redraw( obj, MADF_DRAWOBJECT );
                }
            }   break;

            case MUIA_TextDisplay_ClipUnit:
                data->Clipboard = tag->ti_Data;
                break;

            case MUIA_TextDisplay_AutoCopy:
                data->Flags &= ~TD_AUTOCOPY;
                if( tag->ti_Data )
                    data->Flags |= TD_AUTOCOPY;
                break;

            case MUIA_TextDisplay_HideMark:
                data->Flags &= ~TD_HIDEMARK;
                if( tag->ti_Data )
                    data->Flags |= TD_HIDEMARK;
                break;

            case MUIA_TextDisplay_Contents:
                DoMethod( obj, MUIM_TextDisplay_Flush );

                data->Size   = strlen(( STRPTR )tag->ti_Data );
                data->Buffer = (STRPTR) tag->ti_Data;

                data->OldWidth  = 0;
                data->Flags    |= ( TD_WORDWRAP | TD_DONTFREE );

                MUI_Redraw( obj, MADF_DRAWOBJECT );
                break;

            case MUIA_TextDisplay_TextPen:
                data->TextPenSpec  = (APTR)tag->ti_Data;
                data->Flags       |= TD_NEWTEXTPEN;
                MUI_Redraw( obj, MADF_DRAWOBJECT );
                break;

            case MUIA_TextDisplay_BackPen:
                data->BackPenSpec  = (APTR)tag->ti_Data;
                data->Flags       |= TD_NEWBACKPEN;
                MUI_Redraw( obj, MADF_DRAWOBJECT );
                break;

            case MUIA_TextDisplay_MarkPen:
                data->MarkPenSpec  = (APTR)tag->ti_Data;
                data->Flags       |= TD_NEWMARKPEN;
                MUI_Redraw( obj, MADF_DRAWOBJECT );
                break;

            case MUIA_TextDisplay_TabSize:
                data->TabSize   = MAX( tag->ti_Data, 1 );
                data->Flags    |= TD_WORDWRAP;
                data->OldWidth  = 0;
                MUI_Redraw( obj, MADF_DRAWOBJECT );
                break;
        }
    }

    return( DoSuperMethodA( cl, obj, (Msg) msg ));
}
///
/// TextDisplay_Get
static ULONG TextDisplay_Get( struct IClass *cl, Object *obj, struct opGet *msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    switch ( msg->opg_AttrID ) {

        case MUIA_TextDisplay_NumLines:
            *msg->opg_Storage = data->NumLines;
            return( TRUE );

        case MUIA_TextDisplay_FirstLine:
            *msg->opg_Storage = data->First;
            return( TRUE );

        case MUIA_TextDisplay_Size:
            *msg->opg_Storage = data->Size;
            return( TRUE );

        case MUIA_TextDisplay_Lines:
            *msg->opg_Storage = (ULONG)&data->Lines;
            return( TRUE );

        case MUIA_TextDisplay_ClipUnit:
            *msg->opg_Storage = data->Clipboard;
            return( TRUE );

        case MUIA_TextDisplay_AutoCopy:
            *msg->opg_Storage = ( data->Flags & TD_AUTOCOPY ) ? TRUE : FALSE;
            return( TRUE );

        case MUIA_TextDisplay_HideMark:
            *msg->opg_Storage = ( data->Flags & TD_HIDEMARK ) ? TRUE : FALSE;
            return( TRUE );

        case MUIA_TextDisplay_Contents:
            *msg->opg_Storage = (ULONG)data->Buffer;
            return( TRUE );

        case MUIA_TextDisplay_TextPen:
            *msg->opg_Storage = (ULONG)data->TextPenSpec;
            return( TRUE );

        case MUIA_TextDisplay_BackPen:
            *msg->opg_Storage = (ULONG)data->BackPenSpec;
            return( TRUE );

        case MUIA_TextDisplay_MarkPen:
            *msg->opg_Storage = (ULONG)data->MarkPenSpec;
            return( TRUE );

        case MUIA_TextDisplay_TabSize:
            *msg->opg_Storage = data->TabSize;
            return( TRUE );
    }

    return( DoSuperMethodA( cl,obj, (Msg) msg ));
}
///
/// TextDisplay_Setup
static ULONG TextDisplay_Setup( struct IClass *cl, Object *obj, Msg msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    if(!( DoSuperMethodA( cl, obj, msg )))
        return( FALSE );

    data->TextPen = MUI_ObtainPen( muiRenderInfo( obj ), data->TextPenSpec, 0 );
    data->BackPen = MUI_ObtainPen( muiRenderInfo( obj ), data->BackPenSpec, 0 );
    data->MarkPen = MUI_ObtainPen( muiRenderInfo( obj ), data->MarkPenSpec, 0 );

    MUI_RequestIDCMP( obj, IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY );

    return( TRUE );
}
///
/// TextDisplay_Cleanup
static ULONG TextDisplay_Cleanup( struct IClass *cl, Object *obj, Msg msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    MUI_RejectIDCMP( obj, IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY );

    MUI_ReleasePen( muiRenderInfo( obj ), data->TextPen );
    MUI_ReleasePen( muiRenderInfo( obj ), data->BackPen );
    MUI_ReleasePen( muiRenderInfo( obj ), data->MarkPen );

    return( DoSuperMethodA( cl, obj, msg ));
}
///
/// TextDisplay_AskMinMax
static ULONG TextDisplay_AskMinMax( struct IClass *cl, Object *obj, struct MUIP_AskMinMax *msg )
{
//    struct TextDisplayData *data = INST_DATA( cl, obj );

    DoSuperMethodA( cl, obj, (Msg) msg );

    msg->MinMaxInfo->MinWidth  += ( _font(obj)->tf_XSize * 10 ) + TD_MARGINS;
    msg->MinMaxInfo->DefWidth  += ( _font(obj)->tf_XSize * 10 ) + TD_MARGINS;
    msg->MinMaxInfo->MaxWidth  += MUI_MAXMAX;

    msg->MinMaxInfo->MinHeight += _font(obj)->tf_YSize + TD_YMARGINS;
    msg->MinMaxInfo->DefHeight += _font(obj)->tf_YSize + TD_YMARGINS;
    msg->MinMaxInfo->MaxHeight += MUI_MAXMAX;

    return( 0 );
}
///
/// TextDisplay_HandleInput
static ULONG TextDisplay_HandleInput( struct IClass *cl, Object *obj, struct MUIP_HandleInput *msg )
{
    struct TextDisplayData  *data = INST_DATA( cl, obj );

#define _between(a,x,b) ((x)>=(a) && (x)<=(b))
#define _isinobject(x,y) (_between(_mleft(obj),(x),_mright(obj)) && _between(_mtop(obj),(y),_mbottom(obj)))

    if( msg->imsg ) {

        switch( msg->imsg->Class ) {

            case IDCMP_MOUSEBUTTONS:
                switch( msg->imsg->Code ) {

                    case SELECTDOWN:
                        if( data->Buffer && _isinobject( msg->imsg->MouseX, msg->imsg->MouseY )) {

                            data->Flags &= ~TD_MARKED;
                            data->Flags |= TD_CLEARMARK;

                            MUI_Redraw( obj, MADF_DRAWUPDATE );

                            data->MarkLineStart = data->First + (( msg->imsg->MouseY - _mtop( obj ) - ( TD_YMARGINS / 2 )) / _font( obj )->tf_YSize );
                            data->MarkColStart  = ( msg->imsg->MouseX - _mleft( obj ) - ( TD_MARGINS / 2 )) / _font( obj )->tf_XSize;

                            data->Flags |= TD_MARK;

                            MUI_RequestIDCMP( obj, IDCMP_MOUSEMOVE | IDCMP_INTUITICKS );
                        }
                        return( 0 );

                    default:
                        data->Flags &= ~( TD_GO_UP | TD_GO_DOWN | TD_MARK );
                        MUI_RejectIDCMP( obj, IDCMP_MOUSEMOVE | IDCMP_INTUITICKS );

                        if( data->Flags & TD_MARKED ) {

                            if( data->MarkLineStart > data->MarkLineEnd ) {
                                ULONG   swap;

                                swap                = data->MarkLineEnd;
                                data->MarkLineEnd   = data->MarkLineStart;
                                data->MarkLineStart = swap;

                                swap               = data->MarkColStart;
                                data->MarkColStart = data->MarkColEnd;
                                data->MarkColEnd   = swap;

                            } else if(( data->MarkLineStart == data->MarkLineEnd ) && ( data->MarkColStart > data->MarkColEnd )) {
                                ULONG   swap;

                                swap               = data->MarkColStart;
                                data->MarkColStart = data->MarkColEnd;
                                data->MarkColEnd   = swap;
                            }
                        }

                        if( data->Flags & TD_AUTOCOPY )
                            DoMethod( obj, MUIM_TextDisplay_CopyToClip );
                        return( 0 );
                }
                break;

            case IDCMP_INTUITICKS:
                if( data->Flags & TD_GO_UP )
                    DoMethod( data->Scroller, MUIM_Prop_Decrease, 1 );

                else if( data->Flags & TD_GO_DOWN )
                    DoMethod( data->Scroller, MUIM_Prop_Increase, 1 );

                if(!( data->Flags & ( TD_GO_UP | TD_GO_DOWN )))
                    return( 0 );
                // fall through

            case IDCMP_MOUSEMOVE: {
                ULONG   line_end, col_end;

                data->Flags &= ~( TD_GO_UP | TD_GO_DOWN );
                data->Flags |= TD_MARKED;

                if( msg->imsg->MouseY < _mtop( obj ) + ( TD_YMARGINS / 2 )) {

                    data->Flags |= TD_GO_UP;
                    line_end     = data->First;

                } else if( msg->imsg->MouseY > _mtop( obj ) + ( data->DisplayedLines * _font( obj )->tf_YSize ) + ( TD_MARGINS / 2 )) {

                    data->Flags |= TD_GO_DOWN;
                    line_end     = data->First + data->DisplayedLines;

                    if( line_end > data->NumLines )
                        line_end = data->NumLines;

                } else
                    line_end = data->First + (( msg->imsg->MouseY - _mtop( obj ) - ( TD_YMARGINS / 2 )) / _font( obj )->tf_YSize );

                if( msg->imsg->MouseX < _mleft( obj ) + ( TD_MARGINS / 2 ))
                    col_end = 0;

                else if( msg->imsg->MouseX > _mright( obj ) - ( TD_MARGINS / 2 ))
                    col_end = ( xget( obj, MUIA_Width ) - TD_MARGINS ) / _font( obj )->tf_XSize;

                else
                    col_end = (( msg->imsg->MouseX - _mleft( obj ) - ( TD_MARGINS / 2 )) / _font( obj )->tf_XSize ) /*+ 1*/;

                if(( data->MarkLineEnd != line_end ) || ( data->MarkColEnd != col_end )) {

                    data->MarkLineEnd = line_end;
                    data->MarkColEnd  = col_end;

                    data->Flags      |= TD_CLEARMARK;
                    data->RedrawLine  = line_end - data->First;

                    MUI_Redraw( obj, MADF_DRAWUPDATE );
                }
            }
                return( 0 );

            case IDCMP_RAWKEY:
                switch( msg->imsg->Code ) {

                    case CURSORDOWN:
                        if( msg->imsg->Qualifier & QUAL_CTRL )
                            DoMethod( obj, MUIM_TextDisplay_Scroll, MUIV_TextDisplay_Scroll_Bottom );
                        else if( msg->imsg->Qualifier & QUAL_SHIFT )
                            DoMethod( obj, MUIM_TextDisplay_Scroll, MUIV_TextDisplay_Scroll_PageDown );
                        else if( msg->imsg->Qualifier & QUAL_ALT )
                            DoMethod( data->Scroller, MUIM_Prop_Increase, 10 );
                        else
                            DoMethod( data->Scroller, MUIM_Prop_Increase, 1 );
                        return( 0 );

                    case CURSORUP:
                        if( msg->imsg->Qualifier & QUAL_CTRL )
                            DoMethod( obj, MUIM_TextDisplay_Scroll, MUIV_TextDisplay_Scroll_Top );
                        else if( msg->imsg->Qualifier & QUAL_SHIFT )
                            DoMethod( obj, MUIM_TextDisplay_Scroll, MUIV_TextDisplay_Scroll_PageUp );
                        else if( msg->imsg->Qualifier & QUAL_ALT )
                            DoMethod( data->Scroller, MUIM_Prop_Decrease, 10 );
                        else
                            DoMethod( data->Scroller, MUIM_Prop_Decrease, 1 );
                        return( 0 );
                }
                break;
        }
    }

    return( DoSuperMethodA( cl, obj, (Msg) msg ));
}
///
/// TextDisplay_Show
static ULONG TextDisplay_Show( struct IClass *cl, Object *obj, struct MUIP_Show *msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    data->Flags |= TD_WORDWRAP; // it may be needed

    SetAttrs( data->Scroller,
              MUIA_NoNotify,        TRUE,
              MUIA_Prop_Visible,    data->DisplayedLines,
              MUIA_Prop_Entries,    data->NumLines,
              MUIA_Prop_First,      data->First,
              TAG_DONE );

    return( TRUE );
}
///
/// TextDisplay_Draw
static ULONG TextDisplay_Draw( struct IClass *cl, Object *obj, struct MUIP_Draw *msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    DoSuperMethodA( cl, obj, (Msg) msg );

    if( msg->flags & ( MADF_DRAWUPDATE | MADF_DRAWOBJECT )) {
        ULONG               i, num, start, line_width, line_height, curr_y, in_chunk;
        struct LinesChunk  *chunk;
        UWORD               oldFG, oldBG, oldDR;
        struct RastPort    *rast;

        if( data->Flags & TD_NEWTEXTPEN ) {
            MUI_ReleasePen( muiRenderInfo( obj ), data->TextPen );
            data->TextPen = MUI_ObtainPen( muiRenderInfo( obj ), data->TextPenSpec, 0 );
        }

        if( data->Flags & TD_NEWBACKPEN ) {
            MUI_ReleasePen( muiRenderInfo( obj ), data->BackPen );
            data->BackPen = MUI_ObtainPen( muiRenderInfo( obj ), data->BackPenSpec, 0 );
        }

        if( data->Flags & TD_NEWMARKPEN ) {
            MUI_ReleasePen( muiRenderInfo( obj ), data->MarkPen );
            data->MarkPen = MUI_ObtainPen( muiRenderInfo( obj ), data->MarkPenSpec, 0 );
        }

        data->Flags &= ~( TD_NEWTEXTPEN | TD_NEWMARKPEN | TD_NEWBACKPEN );

        line_width  = _mwidth( obj ) - TD_MARGINS;
        line_height = _font( obj )->tf_YSize;

        data->DisplayedLines = ( _mheight( obj ) - TD_YMARGINS ) / line_height;
        data->XOffset        = _mleft( obj ) + TD_MARGINS / 2;
        data->StartX         = data->XOffset;

        if(( data->OldWidth != _mwidth( obj )) && ( data->Flags & TD_WORDWRAP )) {
            struct LinesChunk  *chunk;

            while( chunk = (struct LinesChunk *) RemTail(( struct List * )&data->Lines ))
                LibFreePooled( data->Pool, chunk, sizeof( *chunk ));

            WordWrap( obj, line_width, data );

            data->Flags &= ~TD_WORDWRAP;
        }

        data->OldWidth = _mwidth( obj );

        num = data->NumLines ? ( data->NumLines - data->First ) : 0;
        num = MIN( data->DisplayedLines, num );

        chunk    = GetNode( &data->Lines, data->First / CHUNK_SIZE );
        in_chunk = data->First % CHUNK_SIZE;

        rast = _rp( obj );

        oldFG = rast->FgPen;
        oldBG = rast->BgPen;
        oldDR = rast->DrawMode;

        SetABPenDrMd( rast, MUIPEN( data->TextPen ), MUIPEN( data->BackPen ), JAM2 );

        SetFont( rast, _font( obj ));

        data->YOffset = rast->TxBaseline + ( TD_YMARGINS / 2 ) + _mtop( obj );


        if(( msg->flags & MADF_DRAWUPDATE ) && ( ABS( data->Direction ) < data->DisplayedLines )) {

            if( data->Flags & TD_CLEARMARK ) {

                start = 0;

            } else if( data->Flags & TD_REDRAWLINE ) {

                start     = data->RedrawLine - data->First;
                num       = start + 1;
                in_chunk += start;

            } else {

                 if( data->Direction > 0 ) {

                    start     = num - data->Direction;
                    in_chunk += start;

                } else {

                    start = 0;
                    num   = -data->Direction;
                }

                ScrollRaster( rast, 0, data->Direction * line_height,
                              _mleft( obj ), _mtop( obj ),
                              _mleft( obj ) + _mwidth( obj ), _mbottom( obj ));
            }

            data->Flags &= ~( TD_CLEARMARK | TD_REDRAWLINE );

        } else {

            SetAPen( rast, MUIPEN( data->BackPen ));

            RectFill( rast,
                      _mleft( obj ), _mtop( obj ),
                      _mleft( obj ) + _mwidth( obj ), _mtop( obj ) + _mheight( obj ));

            SetAPen( rast, MUIPEN( data->TextPen ));

            start = 0;
        }

        if(!( data->NumLines )) {
            SetABPenDrMd( rast, oldFG, oldBG, oldDR );
            return( 0 );
        }

        curr_y = data->YOffset + ( line_height * start );

        if( chunk ) for( i = start; i < num; i++ ) {

            while( in_chunk >= CHUNK_SIZE ) {
                in_chunk -= CHUNK_SIZE;
                chunk    = chunk->Succ;
            }

            Move( rast, data->XOffset, curr_y );

            if( data->Flags & ( TD_MARK | TD_MARKED )) {
                ULONG   real_line, line_start, line_end;
                UWORD   col_start, col_end;

                real_line = data->First + i;

                line_start = data->MarkLineStart;
                line_end   = data->MarkLineEnd;

                col_start = data->MarkColStart;
                col_end   = data->MarkColEnd;

                if( line_start > line_end ) {

                    line_start = data->MarkLineEnd;
                    line_end   = data->MarkLineStart;

                    col_start  = data->MarkColEnd;
                    col_end    = data->MarkColStart;
                }

                if(( line_start <= real_line ) && ( real_line <= line_end )) {
                    STRPTR  txt;
                    ULONG   len, marked;

                    // ok, we're in the 'mark zone'

                    txt = chunk->Lines[ in_chunk ].Line;
                    len = chunk->Lines[ in_chunk ].Length;

                    if( line_start == line_end ) {
                        ULONG   cols;
                        LONG    tmp;

                        //  there's something only in this line

                        if( col_start > col_end ) {
                            col_start = data->MarkColEnd;
                            col_end   = data->MarkColStart;
                        }

                        if( col_start >= len ) {

                            curr_y   += line_height;
                            in_chunk += 1;

                            continue;
                        }

                        tmp     = GetPrintSize( txt, len, data->TabSize, 0 );
                        col_end = MIN( col_end, tmp );

                        cols = GetRealSize( txt, col_start, data->TabSize, 0 );

                        if( cols > 0 ) {
                            //  first part, not marked

                            SetBPen( rast, MUIPEN( data->BackPen ));

                            RastWrite( txt, col_start + 1, rast, _font( obj ), data );

                            len -= cols;
                            txt += cols;
                        }

                        SetBPen( rast, MUIPEN( data->MarkPen ));

                        Move( rast, data->XOffset + ( col_start * _font( obj )->tf_XSize ), curr_y );

                        marked = GetRealSize( txt, col_end - col_start + 1, data->TabSize, col_start );

                        RastWrite( txt, GetPrintSize( txt, marked, data->TabSize, col_start ), rast, _font( obj ), data );

                        marked  = MIN( marked, len );
                        len    -= marked;

                        if( len > 0 ) {
                            //  last part, not marked

                            txt += marked;

                            SetBPen( rast, MUIPEN( data->BackPen ));

                            Move( rast, data->XOffset + (( col_end + 1 ) * _font( obj )->tf_XSize ), curr_y );

                            RastWrite( txt, GetPrintSize( txt, len, data->TabSize, col_end ) + 1, rast, _font( obj ), data );
                        }

                    } else {
                        ULONG   print;

                        print = GetPrintSize( txt, len, data->TabSize, 0 );

                        if(( real_line == line_start ) && ( len < col_start )) {

                            SetBPen( rast, MUIPEN( data->BackPen ));

                            RastWrite( txt, print + 1, rast, _font( obj ), data );

                        } else if(( real_line == line_start ) || ( real_line == line_end )) {
                            ULONG   marked, col;
                            LONG    tmp;

                            SetBPen( rast, MUIPEN(( real_line == line_start ) ? data->BackPen : data->MarkPen ));

                            col    = ( real_line == line_start ) ? col_start : col_end;

                            tmp    = GetRealSize( txt, col, data->TabSize, 0 );
                            marked = MIN( tmp, len );

                            RastWrite( txt, GetPrintSize( txt, marked, data->TabSize, 0 ) + 1, rast, _font( obj ), data );

                            len -= marked;

                            if( len > 0 ) {

                                txt += marked;

                                SetBPen( rast, MUIPEN(( real_line == line_end ) ? data->BackPen : data->MarkPen ));

                                Move( rast, data->XOffset + ( col * _font( obj )->tf_XSize ), curr_y );

                                RastWrite( txt, GetPrintSize( txt, len, data->TabSize, col ) + 1, rast, _font( obj ), data );
                            }

                        } else {
                            // all marked

                            SetBPen( _rp( obj ), MUIPEN( data->MarkPen ));

                            RastWrite( txt, print + 1, rast, _font( obj ), data );
                        }
                    }

                } else {
                    // no marking here

                    SetBPen( rast, MUIPEN( data->BackPen ));

                    RastWrite( chunk->Lines[ in_chunk ].Line, GetPrintSize( chunk->Lines[ in_chunk ].Line, chunk->Lines[ in_chunk ].Length, data->TabSize, 0 ) + 1, rast, _font( obj ), data );
                }

            } else
                RastWrite( chunk->Lines[ in_chunk ].Line, GetPrintSize( chunk->Lines[ in_chunk ].Line, chunk->Lines[ in_chunk ].Length, data->TabSize, 0 ) + 1, rast, _font( obj ), data );

            curr_y   += line_height;
            in_chunk += 1;
        }

        SetABPenDrMd( rast, oldFG, oldBG, oldDR );

        data->Direction = 0;
    }

    return( 0 );
}
///
/// TextDisplay_Scroll
static ULONG TextDisplay_Scroll( struct IClass *cl, Object *obj, struct MUIP_TextDisplay_Scroll *msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );
    ULONG                   first;

    switch( msg->FirstLine ) {

        case MUIV_TextDisplay_Scroll_PageDown:
            first = MIN( data->First + data->DisplayedLines, data->NumLines ) - 1;
            break;

        case MUIV_TextDisplay_Scroll_PageUp:
            first = ( data->First > data->DisplayedLines ) ? ( data->First - data->DisplayedLines + 1 ) : 0;
            break;

        case MUIV_TextDisplay_Scroll_Bottom:
            first = data->NumLines - (( data->NumLines < data->DisplayedLines ) ? 0 : data->DisplayedLines );
            break;

        default:
            first = msg->FirstLine;
            break;
    }

    if( data->NumLines <= data->DisplayedLines )
        first = 0;
    else if( first > data->NumLines - data->DisplayedLines )
        first = data->NumLines - data->DisplayedLines;

    if( data->Direction = first - data->First ) {

        data->First += data->Direction;

        MUI_Redraw( obj, MADF_DRAWUPDATE );

        nnset( data->Scroller, MUIA_Prop_First, data->First );

        notify( data->Notify, obj, MUIA_TextDisplay_FirstLine, data->First, TAG_DONE );
    }

    return( 0 );
}
///
/// TextDisplay_Flush
static ULONG TextDisplay_Flush( struct IClass *cl, Object *obj, Msg msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );
    struct LinesChunk      *chunk;

    while( chunk = (struct LinesChunk *) RemTail(( struct List * )&data->Lines ))
        LibFreePooled( data->Pool, chunk, sizeof( *chunk ));

    if(!( data->Flags & TD_DONTFREE ))
        FreeVec( data->Buffer );

    data->First     = 0;
    data->NumLines  = 0;
    data->Flags    &= TD_PREFSMASK;
    data->Buffer    = NULL;
    data->Size      = 0;

    nnset( data->Scroller, MUIA_Prop_Visible, 1 );
    nnset( data->Scroller, MUIA_Prop_Entries, 1 );

    if(!( data->Flags & TD_DISPOSING ))
        MUI_Redraw( obj, MADF_DRAWOBJECT );

    return( 0 );
}
///
/// TextDisplay_LoadFile
static ULONG TextDisplay_LoadFile( struct IClass *cl, Object *obj, struct MUIP_TextDisplay_LoadFile *msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );
    ULONG                   ret = FALSE;
    BPTR                    lock;

    DoMethod( obj, MUIM_TextDisplay_Flush );

    if( lock = Lock( msg->FileName, ACCESS_READ )) {
        struct FileInfoBlock   *fib;

        if( fib = AllocDosObject( DOS_FIB, NULL )) {
            BPTR    file;

            Examine( lock, fib );

            data->Size = fib->fib_Size;

            if( file = Open( msg->FileName, MODE_OLDFILE )) {

                if( data->Buffer = AllocVec( fib->fib_Size + 1, MEMF_CLEAR )) {

                    Read( file, data->Buffer, fib->fib_Size );

                    data->OldWidth  = 0;
                    data->Flags    |= TD_WORDWRAP;

                    MUI_Redraw( obj, MADF_DRAWOBJECT );

                    ret = TRUE;
                }

                Close( file );
            }

            FreeDosObject( DOS_FIB, fib );
        }

        UnLock( lock );
    }

    return( ret );
}
///
/// TextDisplay_CopyToClip
static ULONG TextDisplay_CopyToClip( struct IClass *cl, Object *obj, Msg msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    if( data->Flags & TD_MARKED ) {
        struct IFFHandle   *iff;

        if( iff = AllocIFF() ) {

            if( iff->iff_Stream = (ULONG) OpenClipboard( data->Clipboard )) {

                InitIFFasClip( iff );

                if(!( OpenIFF( iff, IFFF_WRITE ))) {
                    struct LinesChunk  *chunk;
                    ULONG               in_chunk, line;

                    PushChunk( iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN );
                    PushChunk( iff, 0,       ID_CHRS, IFFSIZE_UNKNOWN );

                    chunk    = GetNode( &data->Lines, data->MarkLineStart / CHUNK_SIZE );
                    in_chunk = data->MarkLineStart % CHUNK_SIZE;
                    line     = data->MarkLineStart;

                    do {
                        ULONG   len;
                        STRPTR  txt;

                        txt = chunk->Lines[ in_chunk ].Line;

                        if( line == data->MarkLineStart ) {
                            ULONG   start;

                            start = GetRealSize( txt, data->MarkColStart, data->TabSize, 0 );

                            if( start >= chunk->Lines[ in_chunk ].Length ) {

                                len = 0;

                            } else {
                                ULONG   end;

                                end = GetRealSize( txt, data->MarkColEnd, data->TabSize, 0 );

                                len  = chunk->Lines[ in_chunk ].Length - start;
                                txt += start;

                                if( line == data->MarkLineEnd )
                                    if( end < chunk->Lines[ in_chunk ].Length )
                                        len = end - start + 1;
                            }

                        } else if( line == data->MarkLineEnd ) {

                            len = GetRealSize( txt, data->MarkColEnd, data->TabSize, 0 );

                            if( len > chunk->Lines[ in_chunk ].Length )
                                len = chunk->Lines[ in_chunk ].Length;

                        } else
                            len = chunk->Lines[ in_chunk ].Length;

                        if( len ) {

                            if((( len + ( txt - chunk->Lines[ in_chunk ].Line )) == chunk->Lines[ in_chunk ].Length ) && ( txt[ len - 1 ] != '\n' ))
                                len += 1;

                            WriteChunkBytes( iff, txt, len );
                        }

                        if( ++in_chunk >= CHUNK_SIZE ) {
                            in_chunk = 0;
                            chunk    = chunk->Succ;
                        }

                    } while( line++ != data->MarkLineEnd );

                    PopChunk( iff );
                    PopChunk( iff );

                    CloseIFF( iff );

                    if( data->Flags & TD_HIDEMARK ) {

                        data->Flags &= ~TD_MARKED;
                        data->Flags |= TD_CLEARMARK;

                        MUI_Redraw( obj, MADF_DRAWUPDATE );
                    }
                }

                CloseClipboard(( struct ClipboardHandle * )iff->iff_Stream );
            }

            FreeIFF( iff );
        }
    }

    return( 0 );
}
///
/// TextDisplay_SetMark
static ULONG TextDisplay_SetMark( struct IClass *cl, Object *obj, struct MUIP_TextDisplay_SetMark *msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    data->MarkLineStart  = msg->LineStart;
    data->MarkLineEnd    = msg->LineEnd;
    data->MarkColStart   = msg->ColStart;
    data->MarkColEnd     = msg->ColEnd;
    data->Flags         |= TD_MARKED;

//    MUI_Redraw( obj, MADF_DRAWUPDATE );

    return( 0 );
}
///
/// TextDisplay_RedrawLine
static ULONG TextDisplay_RedrawLine( struct IClass *cl, Object *obj, struct MUIP_TextDisplay_RedrawLine *msg )
{
    struct TextDisplayData *data = INST_DATA( cl, obj );

    if(( msg->Line < data->NumLines ) && ( msg->Line >= data->First ) && ( msg->Line < MIN( data->NumLines, data->First + data->DisplayedLines ))) {

        data->RedrawLine  = msg->Line;
        data->Flags      |= TD_REDRAWLINE;

        MUI_Redraw( obj, MADF_DRAWUPDATE );
    }

    return( 0 );
}
///


/// GetRealSize
static LONG GetRealSize( STRPTR Txt, ULONG Length, ULONG TabSize, ULONG CurPos )
{
    ULONG   len;
    UBYTE  *ptr;

    ptr = Txt;

    for( len = 0; len <= Length; ) {

        switch( *ptr++ ) {

            case 0x9b:          /*  CSI   */
                goto csi;

            case '\033':        /*  skip the escape sequence */

                switch( *ptr++ ) {

                    case '[':       /* ANSI sequence */
csi:
                        for( ; *ptr; ptr++ ) {

                            while( isdigit( *ptr ))
                                ptr++;

                            if( *ptr != ';')
                                break;
                        }

                        ptr++;
                        break;
                }
                break;

            case '\t':
                len += TabSize - (( CurPos + len ) % TabSize );
                break;

            default:
                len += 1;
                break;
        }
    }

    return( ptr - Txt - 1 );
}
///
/// GetPrintSize
static ULONG GetPrintSize( STRPTR ptr, ULONG Length, ULONG TabSize, ULONG CurPos )
{
    ULONG   len, ret = 0;

    for( len = 0; len < Length; len++ ) {

        switch( *ptr++ ) {

            case 0x9b:          /*  CSI   */
                goto csi;

            case '\033':        /*  skip the escape sequence */

                len++;

                switch( *ptr++ ) {

                    case '[':       /* ANSI sequence */
csi:
                        for( ; ( len < Length ) && *ptr; ptr++, len++ ) {

                            while( isdigit( *ptr )) {
                                ptr++;
                                len++;
                            }

                            if( *ptr != ';')
                                break;
                        }

                        ptr++;
                        len++;
                        break;
                }
                break;

            case '\t':
                ret += TabSize - (( CurPos + ret ) % TabSize );
                break;

            default:
                ret += 1;
                break;
        }
    }

    return( ret );
}
///
/// GetLine
static LONG GetLine( STRPTR Txt, ULONG MaxW, struct TextFont *font, struct TextDisplayData *data )
{
    ULONG               W, FixedW;
    LONG                processati = 0;
    UBYTE              *ptr;

    /* how many chars will fit in the rp? */
    ptr = Txt;
    W   = 0;

    FixedW = font->tf_XSize;

    while( W < MaxW ) {
        UBYTE   c;

        c = *ptr;

        if(( c == '\n' ) || ( c == '\0' ))
            break;

        ++ptr;

        processati += 1;

        switch( c ) {

            case 0x9b:          /*  CSI   */
                goto csi;

            case '\033':        /*  skip the escape sequence */

                switch( *ptr++ ) {
                    case 'c':       /* reset */
                        W = 0;
                        break;
                    case '[':       /* ANSI sequence */
csi:
                        for( ; ( *ptr != '\n' ) && ( *ptr != '\0' ); ptr++ ) {

                            while( isdigit( *ptr ))
                                ptr++;

                            if( *ptr != ';')
                                break;
                        }

                        if( *ptr && ( *ptr != '\n' ))
                            ptr++;
                        break;

                    case '\0':
                    case '\n':
                        c = '\0';
                        ptr--;
                        break;
                }
                break;

            case '\f':  /*  form feed                                       */
            case '\r':  /*  carriage return: start again from left margin   */
                W = 0;
                break;

            case '\t': {
                ULONG   cur_pos, spaces;

                cur_pos = ( W / FixedW );
                spaces  = (( cur_pos + data->TabSize ) & ~( data->TabSize - 1 )) - cur_pos + 1;

                W += spaces * FixedW;
            }
                break;

            default:
                W += FixedW;
                break;
        }

        if( c == '\0' )
            break;

    } /*  while  */


    if( W > MaxW ) {     // a character exceded the limit
        ptr        -= 1;
        processati -= 1;
    }

    if(( *ptr != '\n' ) && ( *ptr != '\0' )) {
        UBYTE *ptr2 = ptr;

        if( *ptr != '\f' ) {

            while( processati-- > 0 ) {

                --ptr;

                if(( *ptr == ' ' ) || ( *ptr == '\t' ))
                    break;
            }
        }

        if( processati <= 0 )
            ptr = ptr2;
    }

    return( ptr - Txt + 1 );
}
///

/// DoCSI
static UBYTE *DoCSI( UBYTE *text, struct RastPort *Rast, struct TextDisplayData *data )
{
    ULONG   arg[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    ULONG   narg = 0, i;

    /* CSI 0 x and CSI x are indistinguishable */
    for( ;; text++ ) {

        while( isdigit( *text )) {
            if( narg < 8 ) {
                arg[ narg ] *= 10;
                arg[ narg ] += ( *text - '0' );
            }
            text++;
        }

        narg += 1;

        if( *text != ';')
            break;
    }


    if( narg > 8 )
        narg = 8;

    switch( *text++ ) {

        case 'm':   /* Select graphic rendition */

            for( i = 0; i < narg; i++ ) {

                switch( arg[ i ]) {
                    case 0:     /* plain */
                        SetSoftStyle( Rast, FS_NORMAL, FSF_BOLD | FSF_ITALIC | FSF_UNDERLINED );
                        SetDrMd( Rast, JAM2 );
                        break;
                    case 1:     /* bold */
                        SetSoftStyle( Rast, FSF_BOLD, FSF_BOLD );
                        break;
                    case 3:     /* italic */
                        SetSoftStyle( Rast, FSF_ITALIC, FSF_ITALIC );
                        break;
                    case 4:     /* underline */
                        SetSoftStyle( Rast, FSF_UNDERLINED, FSF_UNDERLINED );
                        break;
                    case 7:     /* inverse video */
                        SetDrMd( Rast, JAM2 | INVERSVID );
                        break;
                    case 22:     /* not bold */
                        SetSoftStyle( Rast, 0, FSF_BOLD );
                        break;
                    case 23:     /* not italic */
                        SetSoftStyle( Rast, 0, FSF_ITALIC );
                        break;
                    case 24:     /* not underline */
                        SetSoftStyle( Rast, 0, FSF_UNDERLINED );
                        break;
                    case 27:     /* not inverse video */
                        SetDrMd( Rast, JAM2 );
                        break;
                }
            }
            break;

        case 'x':           /* set left offset */
            data->XOffset = data->StartX + arg[ 0 ];
            break;
    }

    return( text );
}
///
/// DoCtrl
static UBYTE *DoCtrl( UBYTE *text, struct RastPort *Rast, struct TextFont *font, struct TextDisplayData *data )
{
    switch( *text++ ) {

        case '\t':          /* tab */
            {
                ULONG   spaces, cur_pos;
                TEXT    spc[20];

                cur_pos = (( Rast->cp_x - data->XOffset ) / font->tf_XSize );
                spaces  = data->TabSize - ( cur_pos % data->TabSize );

                memset( spc, ' ', sizeof( spc ));

                while( spaces ) {
                    ULONG   now;

                    now = MIN( spaces, sizeof( spc ));

                    Text( Rast, spc, now );

                    spaces -= now;
                }
            }
            break;

        case '\n':          /* newline */
            break;

        case '\r':          /* carriage return */
            Move( Rast, data->XOffset, Rast->cp_y );
            break;

        case '\f':          /* formfeed */
            Move( Rast, data->XOffset, data->YOffset );
            break;

        case (UBYTE)'\233': /* control sequence introducer */
            text = DoCSI( text, Rast, data );
            break;

        case '\033':            /* escape */
            switch( *text++ ) {
                case 'c':       /* reset */
                    data->XOffset = data->StartX;
                    SetABPenDrMd( Rast, 1, 0, JAM1 );
                    DoCtrl( "\n", Rast, font, data );
                    DoCSI( "0m", Rast, data );
                    break;
                case '[':       /* CSI */
                    text = DoCSI( text, Rast, data );
                    break;
            }
            break;
    }

    return( text );
}
///
/// RastWrite
static void RastWrite( UBYTE *text, ULONG length, struct RastPort *Rast, struct TextFont *font, struct TextDisplayData *data )
{
    ULONG   i = 0;

    while( i < length ) {
        UBYTE  *p;
        ULONG   len;

        /* First, determine how much real text we have */
        for( len = 0, p = text; ( i < length ) && isprint( *p ); i++, p++ )
            len += 1;

        if( len > 0 ) {
            Text( Rast, text, len );
            text += len;
        }

        if(( *text == '\n' ) || ( *text == '\0' ))
            break;

        if( *text == '\t' )
            i += data->TabSize - ((( Rast->cp_x - data->XOffset ) / font->tf_XSize ) % data->TabSize );

        text = DoCtrl( text, Rast, font, data );
    }
}
///


/// WordWrap
static void WordWrap( Object *obj, ULONG line_width, struct TextDisplayData *data )
{
    UBYTE              *ptr;
    ULONG               numlines = 0, in_chunk = 0;
    LONG                len;
    struct LinesChunk  *chunk = NULL;

    if(!( ptr = data->Buffer ))
        return;

    /*
        Now we'll prepare the line tables with wordwrapping
    */

    len = data->Size + 1; // +1 for the ending '\0'

    do {
        ULONG   now;

        now = GetLine( ptr, line_width, _font( obj ), data );

        if( chunk == NULL ) {

            if( chunk = LibAllocPooled( data->Pool, sizeof( struct LinesChunk ))) {

                AddTail(( struct List * )&data->Lines, ( struct Node * )chunk );

                in_chunk = 0;

            } else
                break;
        }

        chunk->Lines[ in_chunk ].Line   = ptr;
        chunk->Lines[ in_chunk ].Length = now;

        ptr += now;
        len -= now;

        if(( ptr[ -1 ] != '\n' ) && ( ptr[ -1 ] != '\0' ))
            chunk->Lines[ in_chunk ].Length -= 1;

        in_chunk += 1;
        numlines += 1;

        if( in_chunk >= CHUNK_SIZE )
            chunk = NULL;

    } while(( len > 0 ) && *ptr );

    data->NumLines = numlines;

    set( obj, MUIA_TextDisplay_FirstLine, data->First );

    SetAttrs( data->Scroller,
              MUIA_NoNotify,        TRUE,
              MUIA_Prop_Visible,    data->DisplayedLines,
              MUIA_Prop_Entries,    data->NumLines,
              MUIA_Prop_First,      data->First,
              TAG_DONE );
}
///
