/*
 * Copyright (c) 1987, 1988, 1989 Stanford University
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Stanford not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  Stanford makes no representations about
 * the suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
 * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * MyStringBrowser implementation.
 */

//#include "/proj/pdevel/autoc/arch.sunCC.iv/scl_cf.h"
// This is an scl configuration file generated by the configure script
// generated using gnu's autoconf
#ifdef HAVE_CONFIG_H
# include <scl_config.h>
#endif

#include <InterViews/bitmap.h>
#include <InterViews/cursor.h>
#include <InterViews/font.h>

#include <IV-2_6/InterViews/button.h>
#include <IV-2_6/InterViews/strbrowser.h>
#include <IV-2_6/InterViews/painter.h>
#include <IV-2_6/InterViews/perspective.h>
#include <IV-2_6/InterViews/sensor.h>
#include <IV-2_6/InterViews/shape.h>
#include <IV-2_6/InterViews/textdisplay.h>
#include <OS/math.h>
#include <OS/memory.h>

#include <mystrbrowser.h>  // ivfasd/mystrbrowser.h

#include <InterViews/Bitmaps/hand.bm>
#include <InterViews/Bitmaps/handMask.bm>
#include <InterViews/Bitmaps/dfast.bm>
#include <InterViews/Bitmaps/dfastMask.bm>
#include <InterViews/Bitmaps/ufast.bm>
#include <InterViews/Bitmaps/ufastMask.bm>

#include <stream.h>
#include <ctype.h>
//#include <math.h>
#include <stdlib.h>
#include <string.h>

// to help ObjectCenter?
#ifndef HAVE_ABS
extern "C" {
    int  abs( int );
}
#endif

//#ifdef __OBJECTCENTER__
//extern "C" {
////    int  abs(int);
//}
//#endif

#ifdef sdgjvlsbz
extern "C"
{
    extern char * strstr( /* char*, char* */ );
}
#endif

//    extern char *strstr(char*, char*);

#ifndef BUFSIZ
#define BUFSIZ 1024
#endif

char
myToLower( const char c ) {
    if( isupper( c ) ) {
        return ( tolower( c ) );
    } else {
        return ( c );
    }

}

char *
myStrToLower( const char * word ) {
    static char newword [BUFSIZ];
    int i = 0;
//    char ToLower (char c);

    while( word [i] != '\0' ) {
        newword [i] = myToLower( word [i] );
        ++i;
    }
    newword [i] = '\0';
    return ( newword )    ;

}

MyStringBrowser::MyStringBrowser(
    ButtonState * bs, int r, int c, boolean u, int h, const char * d
) {
    Init( bs, r, c, u, h, d );
}

MyStringBrowser::MyStringBrowser(
    const char * name,
    ButtonState * bs, int r, int c, boolean u, int h, const char * d
) {
    SetInstance( name );
    Init( bs, r, c, u, h, d );
}

void MyStringBrowser::Init(
    ButtonState * bs, int r, int c, boolean u, int h, const char * d
) {
    const int defaultSize = 256;

    SetClassName( "MyStringBrowser" );
    input = new Sensor;
    input->Reference();
    input->Catch( DownEvent );
    input->Catch( KeyEvent );

    strbufsize = selbufsize = defaultSize;
    strbuf = new char*[strbufsize];
    selbuf = new char*[selbufsize];
    strcount = selcount = 0;

    display = nil;
    lineheight = 0;
    rows = r;
    columns = c;
    uniqueSel = u;
    singleClick = false;
    highlight = h;
    lastx = lasty = -1;
    subject = bs;
    Resource::ref( subject );
    done = d;
    perspective = new Perspective;
    firstResize = true;
    InitTextDisplay();
}

void MyStringBrowser::InitTextDisplay() {
    delete display;
    display = new TextDisplay;
    display->Draw( output, canvas );
    display->CaretStyle( NoCaret );

    for( int i = 0; i < strcount; ++i ) {
        display->ReplaceText( i, strbuf[i], strlen( strbuf[i] ) );
    }
    if( canvas != nil ) {
        output->ClearRect( canvas, 0, 0, xmax, ymax );
        firstResize = true;
        Resize();
    }
}

MyStringBrowser::~MyStringBrowser() {
    Clear();
    delete strbuf;
    delete display;
    Resource::unref( subject );
    Resource::unref( perspective );
}

static void BufCheck( char ** & buf, int & bufsize, int count, int index ) {
    char ** newbuf;

    if( index >= bufsize ) {
        bufsize = ( index + 1 ) * 2;
        newbuf = new char*[bufsize];
        Memory::copy( buf, newbuf, count * sizeof( char * ) );
//              bcopy(buf, newbuf, count*sizeof(char*));
        delete buf;
        buf = newbuf;
    }
}

static void BufInsert(
    char * s, int index, char ** & buf, int & bufsize, int & count
) {
    char ** spot;
    index = ( index < 0 ) ? count : index;

    if( index < count ) {
        BufCheck( buf, bufsize, count, count + 1 );
        spot = &buf[index];
        Memory::copy( spot, spot + 1, ( count - index )*sizeof( char * ) );
//             bcopy(spot, spot+1, (count - index)*sizeof(char*));

    } else {
        BufCheck( buf, bufsize, count, index );
        spot = &buf[index];
    }
    *spot = ( char * )s;
//    *spot = s;
    ++count;
}

static void BufReplace(
    const char * s, int index, char ** & buf, int & count
//    const char* s, int index, const char**& buf, int& count
) {
    if( index >= 0 && index < count ) {
        if( strlen( buf[index] ) >= strlen( s ) ) {
            strcpy( ( char * )buf[index], ( char * )s );
        } else {
//      delete [index] buf; // ack this deletes the buffer. Dave
            delete buf[index];
            buf[index] = new char[strlen( s ) + 1];
            strcpy( ( char * )buf[index], ( char * )s );
        }
    }
}

//static void BufRemove (int index, const char** buf, int& count) {
static void BufRemove( int index, char ** buf, int & count ) {
    if( index < --count ) {
//        const char** spot = &buf[index];
        char ** spot = &buf[index];
        Memory::copy( spot + 1, spot, ( count - index )*sizeof( char * ) );
//             bcopy(spot+1, spot, (count - index)*sizeof(char*));
    }
}

static int BufFind(
    int index,
    char ** srcbuf, int srccount,
    char ** dstbuf, int dstcount
//    const char** srcbuf, int srccount,
//    const char** dstbuf, int dstcount
) {
    if( 0 <= index && index < srccount ) {
        const char * s = srcbuf[index];

        if( s != nil ) {
            for( int i = 0; i < dstcount; ++i ) {
                if( dstbuf[i] == s ) {
                    return i;
                }
            }
        }
    }
    return -1;
}

void MyStringBrowser::ReplaceText( const char * s, int index ) {
    if( 0 <= index && index < strcount ) {
        register Perspective * p = perspective;

        int SelIndex = BufFind( index, strbuf, strcount, selbuf, selcount );
        BufReplace( s, index, strbuf, strcount ); // replaces line in buffer
        // the buffer now contains a copy of what was in s

        if( output != nil ) {
            p->width = Math::max( p->width, output->GetFont()->Width( s ) );
        }
        p->Update();

        display->ReplaceText( index, s, strlen( s ) );
        if( SelIndex >= 0 ) {
            selbuf[SelIndex] = strbuf[index];
            display->AddStyle( index, 0, index, columns, highlight );
//      display->Style(index, 0, index, columns, highlight);
        }
    }
//    int i;
//    for (i = 0; i < strcount; i++)
//  printf("%d: '%s'\n", i, strbuf[i]);
}

void MyStringBrowser::Insert( const char * s, int index ) {
    display->Draw( output, canvas );
    register Perspective * p = perspective;

    char * copy = new char[strlen( s ) + 1];
    strcpy( copy, s );
    BufInsert( copy, index, strbuf, strbufsize, strcount );

    if( output != nil ) {
        p->width = Math::max( p->width, output->GetFont()->Width( s ) );
    }
    p->height += lineheight;
    p->cury += lineheight;
    p->Update();

    if( index < strcount - 1 ) {
        display->InsertLinesAfter( index - 1, 1 );
    }
    display->ReplaceText( index, s, strlen( s ) );
}

void MyStringBrowser::Append( const char * s ) {
    Insert( s, strcount );
}

void MyStringBrowser::Remove( int index ) {
    if( 0 <= index && index < strcount ) {
        display->Draw( output, canvas );
        register Perspective * p = perspective;
        char * string = String( index );

        if(
            output != nil && p->width > columns * shape->hunits &&
            p->width == output->GetFont()->Width( string )
        ) {
            UpdateWidth();
        }
        Unselect( index );

// BUG fix from original StringBrowser (iv 2.6)... the following two stmts were
//      switched.  It didn't show up I think because
//      TextDisplay remembers its text.
        delete string;
        BufRemove( index, strbuf, strcount );

        p->height -= lineheight;
        p->cury -= lineheight;
        p->Update();
        display->DeleteLinesAfter( index - 1, 1 );
    }
}


int MyStringBrowser::SearchForward( char * substr, int startIndex,
                                    int ignoreCase ) {
    if( strcount <= 0 ) {
        cerr << "strcount <= 0: " << strcount << '\n';
        return ( -1 );
    }

    if( ( startIndex < 0 ) || ( startIndex >= strcount ) ) {
        cerr << "Index is out of bounds";
        return ( -1 );
    }

    int i = startIndex;

    if( ignoreCase ) {
        char substrLower[BUFSIZ];
        strncpy( substrLower, myStrToLower( substr ), BUFSIZ );
        substrLower[BUFSIZ - 1] = 0;

        while( i < strcount ) {
            if( strstr( myStrToLower( strbuf[i] ), substrLower ) ) {
                return i;
            }
            ++i;
        }
        i = 0;
        while( i < startIndex ) {
            if( strstr( myStrToLower( strbuf[i] ), substrLower ) ) {
                return i;
            }
            ++i;
        }
    } else {
        while( i < strcount ) {
            if( strstr( strbuf[i], substr ) ) {
                return i;
            }
            ++i;
        }
        i = 0;
        while( i < startIndex ) {
            if( strstr( strbuf[i], substr ) ) {
                return i;
            }
            ++i;
        }
    }

    return ( -1 );
}

int MyStringBrowser::SearchBackward( char * substr, int startIndex,
                                     int ignoreCase ) {
    if( strcount <= 0 ) {
        cerr << "strcount <= 0: " << strcount << '\n';
        return ( -1 );
    }

    if( ( startIndex < 0 ) || ( startIndex >= strcount ) ) {
        cerr << "Index is out of bounds";
        return ( -1 );
    }

    int i = startIndex;

    if( ignoreCase ) {
        char substrLower[BUFSIZ];
        strncpy( substrLower, myStrToLower( substr ), BUFSIZ );
        substrLower[BUFSIZ - 1] = 0;

        while( i >= 0 ) {
            if( strstr( myStrToLower( strbuf[i] ), substrLower ) ) {
                return i;
            }
            --i;
        }

        i = strcount - 1;
        while( i > startIndex ) {
            if( strstr( myStrToLower( strbuf[i] ), substrLower ) ) {
                return i;
            }
            --i;
        }
    } else {
        while( i >= 0 ) {
            if( strstr( strbuf[i], substr ) ) {
                return i;
            }
            --i;
        }

        i = strcount - 1;
        while( i > startIndex ) {
            if( strstr( strbuf[i], substr ) ) {
                return i;
            }
            --i;
        }
    }

    return ( -1 );
}

void
MyStringBrowser::Bold( int index ) {
    display->Draw( output, canvas );
    display->AddStyle( index, 0, index + 1, -1, Boldface );
}

int MyStringBrowser::Index( const char * s ) {
    for( int i = 0; i < strcount; ++i ) {
        if( strcmp( s, strbuf[i] ) == 0 ) {
            return i;
        }
    }
    return -1;
}

char * MyStringBrowser::String( int index ) {
    return ( 0 <= index && index < strcount ) ? strbuf[index] : nil;
}

void MyStringBrowser::Clear() {
    for( int i = 0; i < strcount; ++i ) {
        delete strbuf[i];
    }
    strcount = selcount = 0;
    InitTextDisplay();

    Perspective np;
    *perspective = np;
    Resize();
}

void MyStringBrowser::Select( int index ) {
    if( index < strcount && !Selected( index ) ) {
        BufInsert( String( index ), selcount, selbuf, selbufsize, selcount );
        display->Draw( output, canvas );
        display->AddStyle( index, 0, index + 1, -1, highlight );
//        display->Style(index, 0, index+1, -1, highlight);
//        display->Style(index, 0, index, columns, highlight);
    }
}

void MyStringBrowser::Unselect( int index ) {
    int selindex;

    if( index < strcount && ( selindex = SelectionIndex( index ) ) >= 0 ) {
        BufRemove( selindex, selbuf, selcount );
        display->Draw( output, canvas );
        display->RemoveStyle( index, 0, index + 1, -1, highlight );
//        display->Style(index, 0, index+1, -1, Plain);
//        display->Style(index, 0, index, columns, Plain);
    }
}

int MyStringBrowser::Selection( int selindex ) {
    return BufFind( selindex, selbuf, selcount, strbuf, strcount );
}

int MyStringBrowser::SelectionIndex( int index ) {
    return BufFind( index, strbuf, strcount, selbuf, selcount );
}

void MyStringBrowser::Browse() {
    Event e;
    e.target = nil;
    e.eventType = EnterEvent;
    Handle( e );
}

boolean MyStringBrowser::HandleDownEvent( Event & e ) {
    boolean finished = true;

    if( e.target == this ) {
        if( e.button == LEFTMOUSE ) {
            finished = LeftButtonDown( e );
        } else if( e.button == MIDDLEMOUSE ) {
            GrabScroll( e );
        } else if( e.button == RIGHTMOUSE ) {
            RateScroll( e );
        }
    } else {
        UnRead( e );
    }
    return finished;
}

boolean MyStringBrowser::HandleKeyEvent( Event & e ) {
    boolean finished = false;

    if( e.len != 0 ) {
        finished = HandleChar( e.keystring[0] );
    }
    return finished;
}

void MyStringBrowser::Handle( Event & e ) {
    if( e.eventType == KeyEvent ) {
        HandleKeyEvent( e );

    } else {
        boolean finished = false;

        do {
            switch( e.eventType ) {
                case DownEvent:
                    finished = HandleDownEvent( e );
                    break;

                case KeyEvent:
                    finished = HandleKeyEvent( e );
                    break;
            }
            if( !finished ) {
                Read( e );
            }
        } while( !finished );
    }
}

boolean MyStringBrowser::HandleChar( char c ) {
    int index = Selection();

    switch( c ) {
        case mySBFirstString:
            ScrollTo( 0 );
            break;
        case mySBLastString:
            ScrollTo( strcount - 1 );
            break;
        case mySBScrollDownf:
        case mySBScrollDownj:
            ScrollBy( 1 );
            break;
        case mySBScrollUpb:
        case mySBScrollUpk:
            ScrollBy( -1 );
            break;
        case mySBSelectAll:
            if( !uniqueSel ) {
                SelectAll();
            }
            break;
        case mySBUnselectAll:
        case mySBUnselectAllAlt:
            UnselectAll();
            break;
        case mySBSelectPreviousString:
            Unselect( index );
            index = Math::max( 0, Math::min( --index, strcount - 1 ) );
//        index = max(0, min(--index, strcount-1));
            Select( index );
            ScrollTo( index );
            break;
        case mySBSelectNextString:
            Unselect( index );
            index = Math::max( 0, Math::min( ++index, strcount - 1 ) );
//        index = max(0, min(++index, strcount-1));
            Select( index );
            ScrollTo( index );
            break;
        case mySBSelectTopString:
            Unselect( index );
            index = Math::max( 0, Locate( 0, ymax ) );
//        index = Locate(0, ymax);
            Select( index );
            break;
        case mySBSelectBottomString:
            Unselect( index );
            index = Math::min( Locate( 0, 0 ), strcount - 1 );
//        index = Locate(0, 0);
            Select( index );
            break;
        case mySBPageDownSp:
        case mySBPageDown:
            ScrollBy( ( ymax + 1 ) / lineheight );
            break;
//    case mySBPageUp:
//        ScrollBy(-(ymax+1) / lineheight);
//        break;
        case mySBHalfPageDown:
            ScrollBy( ( ymax + 1 ) / lineheight / 2 );
            break;
        case mySBHalfPageUp:
            ScrollBy( -( ymax + 1 ) / lineheight / 2 );
            break;
        default:
            if( !HandleCharExtra( c ) ) {
                for( int i = 0; done[i] != '\0'; ++i ) {
                    if( c == done[i] ) {
                        subject->SetValue( c );
                        return true;
                    }
                }
            }
            break;
    }
    return false;
}

void MyStringBrowser::Adjust( Perspective & np ) {
    register Perspective * p = perspective;

    float scale = ( np.height == 0 ) ? 1 : float( p->height ) / float( np.height );
    int x = p->x0 + int( ( np.curx - np.x0 ) * scale );
    int y = p->y0 + int( ( np.cury - np.y0 ) * scale );
    ScrollTo( x, y );

// the above is iv 3.1's replacement for the 2.6 code below...
//    float scale = float(np.height) / float(p->height);
//    ScrollTo(0, p->y0 + int((np.cury - np.y0) / scale));
}

static Cursor * handCursor;
static Cursor * upCursor;
static Cursor * dnCursor;

void MyStringBrowser::Reconfig() {
    if( handCursor == nil ) {
        handCursor = new Cursor(
            new Bitmap(
                hand_bits, hand_width, hand_height, hand_x_hot, hand_y_hot
            ),
            new Bitmap( hand_mask_bits, hand_mask_width, hand_mask_height ),
            output->GetFgColor(), output->GetBgColor()
        );

        upCursor = new Cursor(
            new Bitmap(
                ufast_bits, ufast_width, ufast_height, ufast_x_hot, ufast_y_hot
            ),
            new Bitmap( ufast_mask_bits, ufast_mask_width, ufast_mask_height ),
            output->GetFgColor(), output->GetBgColor()
        );

        dnCursor = new Cursor(
            new Bitmap(
                dfast_bits, dfast_width, dfast_height, dfast_x_hot, dfast_y_hot
            ),
            new Bitmap( dfast_mask_bits, dfast_mask_width, dfast_mask_height ),
            output->GetFgColor(), output->GetBgColor()
        );
    }

    const Font * f = output->GetFont();
    shape->hunits = f->Width( "n" );
    shape->vunits = f->Height();
    lineheight = shape->vunits;
    shape->Rect( shape->hunits * columns, shape->vunits * rows );
    shape->Rigid( hfil, hfil, shape->height - lineheight, vfil );

    const char * attrib = GetAttribute( "singleClick" );
    singleClick = ( attrib != nil && strcmp( attrib, "on" ) == 0 );

    attrib = GetAttribute( "clickDelay" );
    clickDelay = ( attrib == nil ) ? 250 : atoi( attrib );
}

void MyStringBrowser::UpdateWidth() {
    if( output != nil ) {
        Perspective * p = perspective;
        const Font * f = output->GetFont();
        p->width = columns * shape->hunits;

        for( int i = 0; i < Count(); ++i ) {
            const char * s = String( i );
            p->width = Math::max( p->width, f->Width( s ) );
        }
    }
}

void MyStringBrowser::InitPerspective( boolean scroll_to_top ) {
    register Perspective * p = perspective;
    int old_top = p->height - p->cury - p->curheight;

    p->lx = p->curwidth = xmax + 1;
    p->ly = p->curheight = ymax + 1;
    p->sx = shape->hunits;
    p->sy = lineheight;
    p->height = Count() * lineheight;
    UpdateWidth();

    if( scroll_to_top ) {
        p->curx = 0;
        p->cury = p->height - p->curheight;

    } else {
        p->cury = p->height - p->curheight - old_top;
    }

    p->Update();
}

void MyStringBrowser::Resize() {
    InitPerspective( perspective->curwidth == 0 );

    if( lineheight != 0 ) {
        display->Draw( output, canvas );
        display->LineHeight( lineheight );
        display->Resize( 0, -lineheight, xmax, ymax );
    }
    /* old iv2.6 contents of Resize()
        register Perspective* p = perspective;

        p->sx = shape->hunits;
        p->sy = lineheight;
        p->lx = xmax+1;
        p->ly = ymax+1;
        p->width = columns * shape->hunits;
        p->height = Count() * lineheight;
        p->curwidth = xmax+1;
        p->curheight = ymax+1;

        if (firstResize) {
            p->curx = 0;
            p->cury = p->height - p->curheight;
            firstResize = false;
        }
        p->Update();
        display->Draw(output, canvas);
        display->LineHeight(lineheight);
        display->Resize(0, -lineheight, xmax, ymax);
    */
}

void MyStringBrowser::Redraw( IntCoord l, IntCoord b, IntCoord r, IntCoord t ) {
    display->Draw( output, canvas );
    display->Redraw( l, b, r, t );
}

void MyStringBrowser::Select( int dot, int mark ) {
//    for (int i = min(dot, mark); i <= max(dot, mark); ++i) {
    for( int i = Math::min( dot, mark ); i <= Math::max( dot, mark ); ++i ) {
        Select( i );
    }
}

void MyStringBrowser::SelectAll() {
    selcount = 0;
    for( int i = 0; i < strcount; ++i ) {
//        BufInsert(strbuf[i], selcount, selbuf, selbufsize, selcount);
        BufInsert( strbuf[i], i, selbuf, selbufsize, selcount );
    }
    display->Draw( output, canvas );
    display->AddStyle( 0, 0, strcount, -1, highlight );
//    display->Style(0, 0, strcount, -1, highlight);
//    display->Style(0, 0, strcount, 0, highlight);
}

void MyStringBrowser::Unselect( int dot, int mark ) {
//    for (int i = min(dot, mark); i <= max(dot, mark); ++i) {
    for( int i = Math::min( dot, mark ); i <= Math::max( dot, mark ); ++i ) {
        Unselect( i );
    }
}

void MyStringBrowser::UnselectAll() {
    selcount = 0;
    display->Draw( output, canvas );
    display->RemoveStyle( 0, 0, strcount, 0, highlight );
//    display->Style(0, 0, strcount, 0, Plain);
}

void MyStringBrowser::ScrollBy( int dx, int dy ) {
//    ScrollTo(0, perspective->cury + dy);
    register Perspective * p = perspective;
    ScrollTo( p->curx + dx, p->cury + dy );
}

void MyStringBrowser::ScrollBy( int lines ) {
    ScrollBy( 0, -lines * lineheight );
}

void MyStringBrowser::ScrollTo( int x, int y ) {
    register Perspective * p = perspective;
    int minx = 0;
    int maxx = Math::max( minx, p->width - p->curwidth / 2 );
    int maxy = p->height - p->curheight;
    int miny = Math::min( maxy, 1 - lineheight );

    p->curx = Math::max( minx, Math::min( x, maxx ) );
    p->cury = Math::max( miny, Math::min( y, maxy ) );
    p->Update();

    int topmargin = p->height - p->curheight - p->cury;
    int line = ( lineheight == 0 ) ? 0 : topmargin / lineheight;

    display->Draw( output, canvas );
    display->Scroll( line, -p->curx, ymax );

    /* old contents of iv2.6 ScrollTo(int,int)
        register Perspective* p = perspective;
        int maxy = p->height - p->curheight;
        int miny = min(maxy, 1-lineheight);

        p->cury = max(miny, min(y, maxy));
        p->Update();
        int topmargin = p->height - p->curheight - p->cury;
        int line = topmargin / lineheight;
        display->Scroll(line, x, ymax);
    */
}

void MyStringBrowser::ScrollTo( int index ) {
    register Perspective * p = perspective;
    IntCoord y0 = p->y0 + p->cury;
    IntCoord y = p->height - ( index + 1 ) * lineheight - y0;

    if( y > ymax ) {
        ScrollTo( 0, y0 - ( ymax - y ) );
    } else if( y < 0 ) {
        y -= ( p->curheight % lineheight == 0 ) ? 0 : lineheight;
        ScrollTo( 0, y0 - ( -y ) );
    }
}

void MyStringBrowser::ScrollToView( IntCoord x, IntCoord y ) {
    IntCoord dx = x < 0 ? x : x > xmax ? x - xmax : 0;
    IntCoord dy = y < 0 ? y : y > ymax ? y - ymax : 0;
    if( dx != 0 || dy != 0 ) {
        ScrollTo( perspective->curx + dx, perspective->cury + dy );
    }
    /* old contents of iv2.6 ScrollToView()
        register Perspective* p = perspective;

        if (y > ymax) {
            ScrollTo(0, p->y0 + p->cury - (ymax-y));
        } else if (y < 0) {
            ScrollTo(0, p->y0 + p->cury - (-y));
        }
     */
}

int MyStringBrowser::Locate( IntCoord x, IntCoord y ) {
    register Perspective * p = perspective;

//    y = max(p->curheight % lineheight, min(y, p->curheight-1));
    y = Math::max( p->curheight % lineheight, Math::min( y, p->curheight - 1 ) );
    return display->LineNumber( y );
}

void MyStringBrowser::Note( Event & e ) {
    lasttime = e.timestamp;
    lastx = e.x;
    lasty = e.y;
}

boolean MyStringBrowser::DoubleClicked( Event & e ) {
    if( e.eventType != DownEvent ) {
        return false;
    }
    const int distThresh = 4;
    int time = abs( int( e.timestamp - lasttime ) );
    int dist = abs( e.x - lastx ) + abs( e.y - lasty );

    return time < clickDelay && dist < distThresh;
}

void MyStringBrowser::UpdateSelection( int d, int m, int Style ) {
    int oldl = Math::min( lastdot, lastmark );
    int oldr = Math::max( lastdot, lastmark );
    int newl = Math::min( d, m );
    int newr = Math::max( d, m );

    if( newr < oldl || newl > oldr ) {          // no overlap
        if( Style == highlight ) {
            Unselect( oldl, oldr );
        }
        if( Style == highlight ) {
            Select( newl, newr );
        } else {
            Unselect( newl, newr );
        }
    } else {                                    // overlap
        if( newl < oldl ) {
            if( Style == highlight ) {
                Select( newl, oldl );
            } else {
                Unselect( newl, oldl );
            }
        } else if( newl > oldl ) {
            if( Style == highlight ) {
                Unselect( oldl, newl - 1 );
            }
        }
        if( newr > oldr ) {
            if( Style == highlight ) {
                Select( oldr, newr );
            } else {
                Unselect( oldr, newr );
            }
        } else if( newr < oldr ) {
            if( Style == highlight ) {
                Unselect( newr + 1, oldr );
            }
        }
    }
    lastdot = d;
    lastmark = m;
}

boolean MyStringBrowser::LeftButtonDown( Event & e ) {
    boolean status = false;

    if( DoubleClicked( e ) ) {
        subject->SetValue( done[0] );
        status = true;

    } else if( uniqueSel ) {
        if( Selections() == 0 ) {
            Select( Locate( e.x, e.y ) );
        } else {
            Unselect( Selection() );
            if( !e.shift ) {
                Select( Locate( e.x, e.y ) );
            }
        }

    } else {
        lastdot = lastmark = Locate( e.x, e.y );

        if( Selected( lastdot ) && e.shift ) {
            Unselect( lastdot );
            do {
                ScrollToView( e.x, e.y );
                UpdateSelection( lastdot, Locate( e.x, e.y ), Plain );
                Poll( e );
            } while( e.leftmouse );

        } else {
            if( !e.shift ) {
                UnselectAll();
            }
            Select( lastdot );
            do {
                ScrollToView( e.x, e.y );
                UpdateSelection( lastdot, Locate( e.x, e.y ), highlight );
                Poll( e );
            } while( e.leftmouse );
        }
    }
    Note( e );
    if( singleClick ) {
        subject->SetValue( done[0] );
        status = true;
    }
    return status;
}

void MyStringBrowser::GrabScroll( Event & e ) {
    int y = e.y;
//    int x = e.x;
    Cursor * origCursor = GetCursor();
    SetCursor( handCursor );

    do {
        ScrollBy( 0, y - e.y );
//        ScrollBy(x - e.x, y - e.y);
        y = e.y;
//        x = e.x;
        Poll( e );
    } while( e.middlemouse );

    SetCursor( origCursor );
}

void MyStringBrowser::RateScroll( Event & e ) {
    Cursor * origCursor = GetCursor();
    int y = e.y;
//    int x = e.x;

    do {
        ScrollBy( 0, e.y - y );
//        ScrollBy(e.x - x, e.y - y);
        if( e.y - y < 0 ) {
            SetCursor( dnCursor );
        } else {
            SetCursor( upCursor );
        }
        Poll( e );
    } while( e.rightmouse );

    SetCursor( origCursor );
}
