

#include "bigstrv.h"

#include "ui/dsplsurf.h"
#include "ui/font.h"
#include "ui/cntroler.h"
#include "ui/shadorec.h"

#if defined(__GNUC__)
template class CL_Binding0 <BigStringView>;
template class UI_EventBinding1 <BigStringView>;
#endif

typedef CL_Binding0      <BigStringView> Bind0;
typedef UI_EventBinding1 <BigStringView> Bind1;
const short scrollBarWidth = 20;
const UI_ViewID ID_BAR = 101;

BigStringView::BigStringView
    (UI_VObjCollection* parent, const UI_Rectangle& shape, 
     CL_StringSequence& model, UI_ViewID id)
: UI_VObjCollection (parent, shape, id), _modelSeq (model)
{
    Bind0 bind (this, &BigStringView::_ModelChanged);
    _modelSeq.AddDependent (bind);

    UI_Rectangle scrollBarRect (shape.Width () - scrollBarWidth, 0,
                                scrollBarWidth, shape.Height ());
    _scrollBar = new UI_VScrollBar (this, scrollBarRect, ID_BAR);
    Bind1 scrollBind (this, &BigStringView::_Scroll);
    _scrollBar->ClientSet().Add (scrollBind);
    _scrollBar->SmoothScroll() = TRUE;
    _selection = -1;
}

void BigStringView::Initialize ()
{
    _visibleCount    = 0;
}

void BigStringView::_SetupScrollBar ()
{
    long size = _modelSeq.Size();
    _scrollBar->SetVisibility (_visibleCount < size ? TRUE : FALSE);
    _scrollBar->PageAmount() = _visibleCount-1;
    _scrollBar->Range () = CL_Interval
        (0, maxl (_visibleCount,  size) - 1);
    CL_Interval& scrollInterval = (CL_Interval&) _scrollBar->Model();
    long low = minl (scrollInterval.Low(), size-1);
    scrollInterval = CL_Interval (low, low + _visibleCount - 1);
}


bool BigStringView::_DrawClientArea ()
{
    UI_DisplaySurface& sfc = CreateDisplaySurface ();
    short charHeight       = Font().Height();
    if (charHeight <= 1) { // Something wrong
        DestroyDisplaySurface ();
        return FALSE;
    }

    // Determine the box in which the strings will be drawn
    UI_Rectangle box = sfc.DrawingArea ();
    box.Origin(UI_Point (0, 0));
    long boxHeight = box.Height();
    long modelSize  = _modelSeq.Size();
    if (!_visibleCount) {
        // Very first paint event
        _visibleCount   = boxHeight/charHeight;
        _SetupScrollBar ();
    }
    if (_visibleCount < modelSize)
        box.AddToWidth (-scrollBarWidth);
    
    sfc.Brush().Color   (UIColor_White);
    sfc.Brush().Pattern (UIBrush_Solid);
    sfc.Pen().Color(UIColor_Black);
    sfc.DrawRectangle (box, UID_Outline | UID_Fill);

    // Draw the strings
    long topElement = _visibleCount >= modelSize ? 0
        : ((CL_Interval&) _scrollBar->Model ()).Low ();
    short yOffset = 0;
    short count = minl (topElement + _visibleCount, modelSize);
    short textWidth = box.Width()-2; // text rectangle is narrower than box
    for (long i = topElement; i < count
         && yOffset + charHeight <= boxHeight; i++, yOffset += charHeight) {
        UI_Rectangle textRect (1, yOffset, textWidth, charHeight);
        sfc.WriteString (_modelSeq[i], textRect);
    }

    // Invert the selected string, if any
    if (_selection >= 0) {
        short index = _selection - topElement;
        if (index >= 0 && index < _visibleCount) {
            UI_Rectangle selectRect (1, index * charHeight,
                                     textWidth, charHeight);
            sfc.InvertRectangle (selectRect);
        }
    }

    // All done
    DestroyDisplaySurface ();
    return TRUE;
}

bool BigStringView::_ModelChanged ()
{
    _SetupScrollBar ();
    _DrawClientArea ();
    return TRUE;
}

bool BigStringView::_Scroll (UI_Event& e)
{
    if (e.Type() != Event_FinishScroll)  // Avoid duplicate drawing
        _DrawClientArea ();
    return TRUE;
}

bool BigStringView::Paint (const UI_Rectangle&)
{
    return _DrawClientArea ();
}

bool BigStringView::ButtonDown (const UI_Point& position, UI_MouseButton btn,
                                bool, bool)
{
    if (btn != UIM_Left)
        return FALSE;
    short charHeight = Font().Height();
    if (charHeight <= 1)
        return FALSE;  // Safety check
    short index = position.YCoord() / charHeight;
    if (index >= _visibleCount)
        return FALSE;
    long topElement = _visibleCount >= _modelSeq.Size() ? 0
        : ((CL_Interval&) _scrollBar->Model()).Low();
    long newSelection = topElement + index;
    if (newSelection >= _modelSeq.Size())
        return FALSE;
    if (newSelection != _selection) {
        UI_DisplaySurface& sfc = CreateDisplaySurface ();
        short charHeight       = Font().Height();
        short width = _shape.Width()-2;
        if (_scrollBar->IsVisible())
            width -= scrollBarWidth;
        if (_selection >= topElement &&
            _selection < topElement + _visibleCount) {
            // Old selection exists and is visible, so de-highlight it
            UI_Rectangle oldSelRect (1, (_selection - topElement) * charHeight,
                                     width, charHeight);
            sfc.InvertRectangle (oldSelRect);
        }                                     
        _selection = newSelection;
        UI_Rectangle selRect (1, (newSelection - topElement) * charHeight,
                              width, charHeight);
        sfc.InvertRectangle (selRect);
    }

    _Controller->AddEvent (new UI_Event (Event_Select, this, this));
    return TRUE;
}

