/*
 * FieldView.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include "stdafx.h"
#include "FieldView.h"
#include "swt.h"
#include "resource.h"
#include "FieldViewItem.h"
#include "Node.h"
#include "Proto.h"
#include "Field.h"
#include "FieldValue.h"
#include "FieldCommand.h"
#include "MFieldCommand.h"
#include "Path.h"
#include "Scene.h"
#include "SFNode.h"

#define CB_WIDTH  9
#define CB_HEIGHT  9
#define CB_SPACING 3
#define CB_ARRAY_INSERT (CB_WIDTH + CB_SPACING * 3)

static void
headerCallback(void *data, int pos, int width)
{
    ((FieldView *) data)->OnHeaderChange(pos, width);
}

static void
textCommandCallback(void *data, int command)
{
    if (command) {
        ((FieldView *) data)->StopEditing();
    } else {
       ((FieldView *) data)->AbortEditing();
    }
}

static void
textFocusCallback(void *data, int value)
{
    if (!value) ((FieldView *) data)->StopEditing();
}

static void
expose(void *data, int x, int y, int width, int height)
{
    ((FieldView *) data)->OnDraw(x, y, width, height);
}

static void
keyCB(void *data, int key, int value, int x, int y, int modifiers)
{
    if (key == SW_MOUSE1) {
        if (value) {
            ((SceneView *) data)->OnLButtonDown(x, y, modifiers);
        } else {
            ((SceneView *) data)->OnLButtonUp(x, y, modifiers);
        }
    } else if (value) {
        ((SceneView *) data)->OnKeyDown(key, x, y, modifiers);
    } else {
        ((SceneView *) data)->OnKeyUp(key, x, y, modifiers);
    }
}

static void
mouseCB(void *data, int x, int y, int modifiers)
{
    ((SceneView *) data)->OnMouseMove(x, y, modifiers);
}

static void
mouseEnterCB(void *data, int value)
{
    if (value) {
        ((SceneView *) data)->OnMouseEnter();
    } else {
        ((SceneView *) data)->OnMouseLeave();
    }
}

FieldView::FieldView(Scene *scene, SWND parent)
  : SceneView(scene, parent)
{
    int width, height;
    int fg, bg;

    swGetSize(parent, &width, &height);

    _scroller = swCreateScrolledWindow(0, 0, width, height, parent);
    _window = swCreateCanvas("", 0, 0, width, height, _scroller);
    swScrolledWindowSetChild(_scroller, _window);
    swSetExposeCallback(_window, expose);
    swSetKeyCallback(_window, keyCB);
    swSetMouseCallback(_window, mouseCB);
    swSetEnterCallback(_window, mouseEnterCB);
    swSetClientData(_window, this);

    _state = NORMAL;
    _trackPoint.x = _trackPoint.y = 0;
    _trackValue = NULL;
    _selectedField = -1;
    _selectedNode = NULL;
    _selectedItem = -1;
    _pageHeight = 0;
    _scrollY = 0;
    _scrollRatio = 1.0f;
    fg = swGetWindowColor(parent, SW_COLOR_WINDOW_FG);
    bg = swGetWindowColor(parent, SW_COLOR_WINDOW_BG);
    int mapFrom[2] = { 0x00ffffff, 0x00ff0000 };
    int mapTo[2] = { fg, bg };
    _halftoneBitmap = swLoadMappedBitmap(parent, IDB_HALFTONE,
                                         mapFrom, mapTo, 2);
    _itemHeight = swGetFontHeight(swGetDefaultFont()) + 2;
    _floatWidth = swGetFontHeight(swGetDefaultFont()) * 4;
    _height = _itemHeight;
    _header = NULL; // this is so we can handle OnSize() correctly
    _header = swCreateHeader(0, 0, width, _itemHeight, _window);
    swHeaderSetCallback(_header, headerCallback);
    swHeaderSetClientData(_header, this);
    int cx = MIN(width, 120);
    swHeaderInsertItem(_header, 0, "Field", cx);
    swHeaderInsertItem(_header, 1, "Value", width - cx);

    _width = 350;

    _uFont = swFindFont("Helvetica", SW_UNDERLINE, 10);
    _fieldViewActive=true;

    _cursorArrow = swLoadCursor(SW_CURSOR_ARROW);
    _cursorHMove = swLoadCursor(SW_CURSOR_DBL_ARROW_HORZ);
    _cursorIsArrow = true;
}

FieldView::~FieldView()
{
    DeleteView();
    DeleteFields();
    swDestroyCursor(_cursorArrow);
    swDestroyCursor(_cursorHMove);
//    swDestroyWindow(_window);
//    _window = NULL;
//    swDestroyWindow(_scroller);
//    _scroller = NULL;
//    swDestroyWindow(_wnd);
//    _wnd = NULL;
}

void FieldView::DeleteFields()
{
    for (int i = 0; i < _fields.size(); i++) {
       delete _fields[i];
    }
    _fields.resize(0);
}

void FieldView::DeleteView()
{
    _fieldViewActive=false;
    if (_halftoneBitmap!=NULL) {
       swDestroyBitmap(_halftoneBitmap);
       _halftoneBitmap=NULL;
    }
    if (_header!=NULL) {
       swHeaderSetCallback(_header,NULL);
       swDestroyHeader(_header);
       _header=NULL;
    }
    if (_uFont!=NULL) {
       swDeleteFont(_uFont);
       _uFont=NULL;
    }
    if (_window != NULL)
       swDeleteCallbacks(_window);
    if (_scroller != NULL)
       swDeleteCallbacks(_scroller);
    if (_wnd != NULL)
       swDeleteCallbacks(_wnd);
    swDestroyWindow(_window);
    _window = NULL;
    swDestroyWindow(_scroller);
    _scroller = NULL;
}

void FieldView::OnDraw(int x, int y, int width, int height)
{
    int n = _items.size();
    
    SDC dc = swCreateDC(_window);
    SDC bdc = swCreateBitmapDC(dc, x + width, y + height);
    if (bdc == NULL) {
        swDestroyDC(dc);
        return;
    }
    swSetFGColor(bdc, swGetWindowColor(_window, SW_COLOR_WINDOW_BG));
    swFillRect(bdc, x, MAX(y, _itemHeight), width, height);
    for (int i = _scrollY; i < _scrollY + _items.size() + 1 && i < n; i++) {
        DrawItem(i, bdc);
    }
#ifndef HAVE_OLPC
    swCopyRect(bdc, dc, x, y, x, y, width, height);
#endif
    swDestroyDC(bdc);
    swDestroyDC(dc);
}

int FieldView::FindField(int field)
{
    int currentField = -1;
    for (int i = 0; i < _fields.size(); i++)
        if (_fields[i]->GetIndex() == field) {
           currentField = i;
           break;
        }
    return currentField;
}

void FieldView::OnUpdate(SceneView *sender, int type, Hint *hint) 
{
    const Path *sel = _scene->getSelection();
    Node *node = sel ? sel->getNode() : NULL;
    int field = sel ? sel->getField() : -1;
    Rect rect;
    FieldUpdate *fieldUpdate;
    NodeUpdate *nodeUpdate;
    int currentField = -1;

    if (_state == EDITING) StopEditing();

    switch (type) {
      CASE_UPDATE(UPDATE_ALL)
        UpdateAll();
        break;
      CASE_UPDATE(UPDATE_SELECTION)
        if (_selectedNode != node || _selectedField != field) {
            _selectedNode = node;
            _selectedField = field;
            swSetScrollPosition(_scroller, 0, 0);
            UpdateAll();
        }
        break;
      CASE_UPDATE(UPDATE_FIELD)
        fieldUpdate = (FieldUpdate *) hint;
        currentField = FindField(fieldUpdate->field);
        if ((fieldUpdate->node == node) && (currentField != -1)) {
            if (fieldUpdate->index == -1) {
                FieldViewItem *item = _fields[currentField];
                FieldValue *value = node->getField(fieldUpdate->field);
                item->SetValue(value);
                if (item->IsGroup() && !item->IsCollapsed()) {
                    if (_fieldViewActive)
                        swInvalidateWindow(_window);
                }
            } else {
                MFieldValue *value = (MFieldValue *) node->getField(fieldUpdate->field);
                MFieldViewItem *parent = (MFieldViewItem *) _fields[currentField];
                parent->GetChild(fieldUpdate->index)->SetValue(value->getSFValue(fieldUpdate->index));
            }
            if (_fieldViewActive) {
                RefreshItemList();
                GetFieldRect(currentField, fieldUpdate->index, &rect);
                swInvalidateRect(_window, rect.left, rect.top,
                                 rect.Width(), rect.Height());
            }
        }
        break;
      CASE_UPDATE(UPDATE_CHANGE_INTERFACE_NODE)
        UpdateAll();
        break;
      CASE_UPDATE(UPDATE_ADD_NODE)
      CASE_UPDATE(UPDATE_REMOVE_NODE)
        nodeUpdate = (NodeUpdate *) hint;
        currentField = FindField(nodeUpdate->field);
        if (nodeUpdate->parent == node) {
            if (nodeUpdate->field)
                GetFieldRect(currentField, -1, &rect);
            RefreshItemList();
            swInvalidateRect(_window, rect.left, rect.top,
                             rect.Width(), rect.Height());
        }
        break;
      CASE_UPDATE(UPDATE_REDRAW)
        UpdateAll();
        swInvalidateWindow(_window);
        break;
    }
}

void FieldView::UpdateAll()
{
    int width0 = GetColumnWidth(0);
    int width1 = GetColumnWidth(1);

/*
    Array<MyString> collapsedNames;
    if (_selectedNode) {
        Proto *def = _selectedNode->getProto();
        if (def) {
            for (int i = 0; i < _fields.size(); i++)
                if (_fields[i]->IsCollapsed() && 
                    (def->getField(i)->getName()->getlength() != 0))
                    collapsedNames.append(mystrdup(def->getField(i)->getName()));
        }
    }                
*/

    DeleteFields();

    if (_selectedNode) {
        Proto *def = _selectedNode->getProto();
        if (def) {            
            _fields.resize(0);
            int numFields = 0;
            for (int i = 0; i < def->getNumFields(); i++) {
                Field *field = def->getField(i);
                if (_scene->isInvalidElement(field))
                    continue;
                FieldValue *value = _selectedNode->getField(i);
                Rect r1;
                GetItemRect(numFields, &r1);
                Rect rect(width0, r1.top, width0 + width1 - 1, r1.bottom);
                _fields.append(FieldViewItem::CreateItem(field, this));
                _fields[numFields]->SetIndex(i);
                _fields[numFields]->CreateControl(rect, _window);
                _fields[numFields]->SetValue(value);
/*
                for (int j = 0; j < collapsedNames.size(); j++)
                    if (strcmp(collapsedNames[j], field->getName()) == 0)
                        _fields[numFields]->SetFlag(FVIS_COLLAPSED);
*/
                numFields++;
            }
        }
    }

    _isRoot = false;
    if (_selectedNode == _scene->getRoot())
        _isRoot = true;    

    _selectedItem = _selectedField = -1;

    RefreshItemList();
    swInvalidateWindow(_window);
}

void FieldView::DrawItem(int index, SDC dc)
{
    bool x3d = _selectedNode->getScene()->isX3d();
    Proto *def = _selectedNode->getProto();
    Field *field;
    FieldViewItem *item = _items[index];
    FieldViewItem *parent = item->GetParent();
    int cx = GetColumnWidth(0);
    Rect r;

    GetItemRect(index, &r);

    swSetClipRect(dc, 0, r.top, r.Width(), _itemHeight);

    if (_isRoot)
        return;

    if (parent) { // item is a MField entry
        field = def->getField(parent->GetIndex());
    } else {
        field = def->getField(item->GetIndex());
        parent = item;
    }

/*
    if ((field->getFlags() & FF_X3D_ONLY) && !x3d) {
        swSetFGColor(dc, 0x000000);
        swFillPatternRect(dc, 0, r.bottom, r.Width(), 1, _halftoneBitmap);
        swFillPatternRect(dc, cx-1, r.top, 1, r.Height(), _halftoneBitmap);
        return;
    }
*/
    int fg;
    if (item->GetState() & FVIS_SELECTED) {
        int highlight = swGetWindowColor(_window, SW_COLOR_HIGHLIGHT);
        swSetFGColor(dc, highlight);
        swFillRect(dc, r.left, r.top, r.Width(), r.Height());
        swSetBGColor(dc, highlight);
        fg = swGetWindowColor(_window, SW_COLOR_HIGHLIGHT_TEXT);
        swSetFGColor(dc, fg);
    } else {
        int bg = swGetWindowColor(_window, SW_COLOR_WINDOW_BG);
        swSetFGColor(dc, bg);
        swFillRect(dc, r.left, r.top, r.Width(), r.Height());
        swSetBGColor(dc, bg);
        if (parent->GetValue())
            if (parent->GetValue()->equals(field->getDefault(x3d))) {
                fg = swGetWindowColor(_window, SW_COLOR_HIGHLIGHT);
            } else {
                fg = swGetWindowColor(_window, SW_COLOR_TEXT);
            }
        swSetFGColor(dc, fg);
    }

    swSetClipRect(dc, 0, r.top, cx, _itemHeight);


    if (item->IsGroup() && ((MFieldViewItem *) item)->GetNumChildren() > 0) {
        // draw a collapse box
        if (item->IsCollapsed()) {
            swDrawPlusBox(dc, CB_SPACING, r.top + (_itemHeight - CB_HEIGHT) / 2);
        } else {
            swDrawMinusBox(dc, CB_SPACING, r.top + (_itemHeight - CB_HEIGHT) / 2);
        }
        swSetFGColor(dc, fg);
    }

    MyString str;
    int x = CB_WIDTH + CB_SPACING * 2;
    int y = r.bottom - 2;

    if (item->GetParent()) {
        // it's an element of an MField; draw a plus sign to insert a new index
        int extraSpace = 0;
        if (item->IsCollapsed()) {
            extraSpace = CB_ARRAY_INSERT;
            swDrawPlusBox(dc, extraSpace, 
                          r.top + (_itemHeight - CB_HEIGHT) / 2);
            swSetFGColor(dc, fg);
        }

        // it's an element of an MField; draw the array index, right-justified
        char buf[128];
        mysnprintf(buf, 128, "[%d] ", item->GetIndex());
        swDrawText(dc, x + extraSpace, y, buf);
    } else {
        // is a parent node, draw the name
        swDrawTextTruncated(dc, x, r.top, cx - x, r.Height() - 1, 
                            field->getName(x3d));
    }

    swSetClipRect(dc, r.left, r.top, r.Width(), _itemHeight);

    item->Draw(dc, cx + 2, r.top);
    swSetFGColor(dc, 0x000000);
    swFillPatternRect(dc, 0, r.bottom, r.Width(), 1, _halftoneBitmap);
    swFillPatternRect(dc, cx-1, r.top, 1, r.Height(), _halftoneBitmap);
}

void FieldView::OnHeaderChange(int index, int width)
{
    if (index == 0) {
        int w, h;
        swHeaderGetSize(_header, &w, &h);
        swHeaderSetItemWidth(_header, 0, width);
        swHeaderSetItemWidth(_header, 1, w - width);
        MoveControls(width);
        swInvalidateRect(_window, 0, h, w, _height);
    }
}

int FieldView::GetColumnWidth(int column) const
{
    return swHeaderGetItemWidth(_header, column);
}

void FieldView::SetColumnWidth(int column, int width)
{
    swHeaderSetItemWidth(_header, column, width);
}

void FieldView::MoveControls(int left)
{
    Rect r;

    for (int i = 0; i < _items.size(); i++) {
        GetItemRect(i, &r);
        _items[i]->MoveControl(left, r.top);
    }
}

int FieldView::HitTest(int x, int y)
{
    int width, height;

    swHeaderGetSize(_header, &width, &height);
    int pos = (y - height) / _itemHeight;

    if (pos >= 0 && pos < _items.size()) {
        return pos;
    } else {
        return -1;
    }
}

void FieldView::GetItemRect(int i, Rect *r)
{
    int width, height;

    swGetSize(_window, &width, &height);
    r->top = (i+1 - _scrollY)*_itemHeight;
    r->bottom = r->top + _itemHeight - 1;
    r->left = 0;
    r->right = width - 1;


}

void FieldView::GetFieldRect(int field, int index, Rect *r)
{
    int width, height;
    swHeaderGetSize(_header, &width, &height);
    int top = height - _scrollY * _itemHeight;

    for (int i = 0; i < field; i++) {
        if (_fields[i]->IsGroup() && !_fields[i]->IsCollapsed()) {
            top += (((MFieldViewItem *) _fields[i])->GetNumChildren() + 1) * _itemHeight;
        } else {
            top += _itemHeight;
        }
    }

    if (index == -1) {
        if ((field != -1) &&
            (_fields[field]->IsGroup() && !_fields[field]->IsCollapsed())) {
            r->top = top;
            r->bottom = (((MFieldViewItem *) _fields[field])->GetNumChildren() + 1) * _itemHeight - 1;
        } else {
            r->top = top;
            r->bottom = top + _itemHeight - 1;
        }
    } else {
        r->top = top + (index+1) * _itemHeight;
        r->bottom = r->top + _itemHeight - 1;
    }
    r->left = 0;
    r->right = width - 1;
}

void FieldView::OnSize(int width, int height) 
{
    swSetSize(_scroller, width, height);
    swSetScrollSizes(_scroller, _width, height);

    int widthReduce = 0;
    if (_header) {
        swHeaderSetSize(_header, width, _itemHeight);
        widthReduce = swHeaderGetItemWidth(_header, 0);
        swHeaderSetItemWidth(_header, 1, width - widthReduce);
    }
    _pageHeight = height / _itemHeight;
    UpdateBars();
}

void FieldView::UpdateBars()
{
    static bool inUpdate = false;
    
    if (!inUpdate) {
        inUpdate = true;

        int x, y, width, height;

        swGetPosition(_window, &x, &y);
        swGetSize(_window, &width, &height);

        if (_height > y + height) {
//            SCROLLINFO info;
//            info.cbSize = sizeof(SCROLLINFO);
//            info.fMask = SIF_PAGE | SIF_RANGE;
//            info.nMin = 0;
//            if (_items.size() > 32767) {
//                info.nMax = 32767;
             _scrollRatio = _items.size() / 32767.0f;
//            } else {
//              info.nMax = _items.size();
                _scrollRatio = 1.0f;
//            }
//            info.nPage = _pageHeight;
//            SetScrollInfo(SB_VERT, &info);
            if (_items.size() > _pageHeight) {
//                EnableScrollBarCtrl(SB_VERT, TRUE);
            } else {
//                EnableScrollBarCtrl(SB_VERT, FALSE);
                _scrollY = 0;
            }
        } else {
//            EnableScrollBarCtrl(SB_VERT, FALSE);
            _scrollY = 0;
        }

        inUpdate = false;
    }
}

void FieldView::StartEditing()
{
    Rect r;
    int cx = GetColumnWidth(0);
    MyString buf;
    
    _scene->UpdateViews(this, UPDATE_START_FIELD_EDIT);
    FieldViewItem      *item = _items[_selectedItem];
    _selectedOffset = item->GetFieldOffset(_trackPoint.x - cx);

    GetItemRect(_selectedItem, &r);   

    item->StartEditing(buf, _selectedOffset);

#ifdef _WIN32
    _edit = swCreateTextEdit(SW_SINGLE_LINE,
                             cx + _selectedOffset * _floatWidth, r.top-4,
                             r.Width(), r.Height() + 8, _window);
#else
    _edit = swCreateTextEdit(SW_SINGLE_LINE,
                             cx + _selectedOffset * _floatWidth, r.top,
                             r.Width(), r.Height(), _window);
#endif

    swSetText(_edit, buf);
    swTextEditSetSelection(_edit, 0, strlen(buf));
    swSetCommandCallback(_edit, textCommandCallback);
    swSetFocusCallback(_edit, textFocusCallback);
    swSetClientData(_edit, this);
    swSetFocus(_edit);
    swEnableAccelerators(FALSE);
    _state = EDITING;
}

void FieldView::StopEditing()
{
    if (_state != EDITING) return;

    Node *node = _selectedNode;
    static FieldUpdate hint(node, _selectedField, _selectedIndex);
    _scene->UpdateViews(this, UPDATE_STOP_FIELD_EDIT, (Hint *) &hint);
    FieldValue *newValue;
    char str[128];

    _state = NORMAL;
    swGetText(_edit, str, 128);
    newValue = _items[_selectedItem]->StopEditing(str, _selectedOffset);

    if (newValue != NULL) {
        if (_items[_selectedItem]->GetParent()) {
            MFieldValue *mvalue = (MFieldValue *) 
                                  _selectedNode->getField(_selectedField);
            mvalue->setSFValue(_selectedIndex, newValue);
            _items[_selectedItem]->SetValue(newValue);
            _scene->setField(_selectedNode, _selectedField, mvalue);
            swInvalidateWindow(_window);
        } else {
            _scene->backupField(_selectedNode, _selectedField);
            _scene->setField(_selectedNode, _selectedField, newValue);
        }
    }
    swDestroyWindow(_edit);
    swEnableAccelerators(TRUE);
    if (!_items[_selectedItem]->IsCollapsed())
         RefreshItemList();
    swInvalidateWindow(_window);
}

void FieldView::AbortEditing()
{
    _state = NORMAL;
    swDestroyWindow(_edit);
}

void FieldView::OnLButtonDown(int x, int y, int modifiers) 
{
    y += _scrollY * _itemHeight;
    int hit = HitTest(x, y);
    Rect    r;

    if (_state == EDITING) {
        StopEditing();
    } else if (hit != -1) {
        FieldViewItem *item = _items[hit];
        if (_selectedItem != hit) {
            // unhighlight old selection, if any
            if (_selectedItem != -1) {
                _items[_selectedItem]->ClearFlag(FVIS_SELECTED);
                GetItemRect(_selectedItem, &r);
                swInvalidateRect(_window, r.left, r.top, r.Width(), r.Height());
            }

            // highlight new selection
            item->SetFlag(FVIS_SELECTED);
            _selectedItem = hit;
            GetItemRect(hit, &r);
            swInvalidateRect(_window, r.left, r.top, r.Width(), r.Height());
        }

        bool colorcircle_flag=false;
        if (item->GetParent()) {
            // selected item is a child of an MField; 
            // use the parent's field index
            _selectedField = item->GetParent()->GetIndex();
            _selectedIndex = item->GetIndex();
            _scene->setViewOfLastSelection(this);
            if (item->IsCollapsed() && 
                (x > CB_ARRAY_INSERT) && (x < CB_ARRAY_INSERT + CB_WIDTH) &&
                (_selectedNode->getField(_selectedField)->getType() != MFNODE)
               ) {
                // add to array box was picked
                if (item->GetParent() != NULL) {
                    ((MFieldViewItem *)item->GetParent())->InsertItem(this, 
                    _selectedIndex + 1);
                    _selectedNode->update();
                }
                RefreshItemList();
                // move all controls below the item toggled
                MoveControls(GetColumnWidth(0));
                swInvalidateWindow(_window);
            } 
            FieldUpdate hint(_scene->getSelection()->getNode(),
                             _selectedField,_selectedIndex);
            _scene->UpdateViews(this,UPDATE_SELECTED_FIELD, (Hint *) &hint);
            if (_selectedNode->getField(_selectedField)->getType() == MFCOLOR){
               colorcircle_flag = true;
               _scene->UpdateViews(this, UPDATE_ENABLE_COLOR_CIRCLE,
                                   (Hint *) &hint);
            }
        } else {
            // otherwise it's a SField, use its own index
            _selectedField = item->GetIndex();
            _selectedIndex = -1;
            _scene->setViewOfLastSelection(this);
            FieldUpdate hint(_scene->getSelection()->getNode(), _selectedField);
            _scene->UpdateViews(this, UPDATE_SELECTED_FIELD, (Hint *) &hint);
            if (_selectedNode->getField(_selectedField)->getType() == SFCOLOR) {
               colorcircle_flag = true;
               _scene->UpdateViews(this, UPDATE_ENABLE_COLOR_CIRCLE,
                                   (Hint *) &hint);
            }
        }
        if (!colorcircle_flag)
           _scene->UpdateViews(this,UPDATE_DISABLE_COLOR_CIRCLE);

        GetItemRect(hit, &r);
        int width0 = GetColumnWidth(0);
        if (x > width0) {
            if (item->IsClickable())
                item->OnClicked(_selectedIndex);
            FieldValue *value = item->OnMouseDown(x - width0, y - r.top, 0);
            if (value && !value->equals(item->GetValue())) {
                if (item->GetParent()) {
                    _scene->execute(new MFieldCommand(_selectedNode, 
                                                      _selectedField, 
                                                      _selectedIndex, value));
                } else {
                    _scene->execute(new FieldCommand(_selectedNode, 
                                                     _selectedField, value));
                }
            } else if (item->IsEditable()) {
                _state = WAIT_EDIT;
            } else if (item->IsTrackable()) {
                _state = WAIT_TRACK;
            } else if (item->GetValue()->getType() == SFNODE) {
                Node *child = ((SFNode *) item->GetValue())->getValue();

                if (child) {
                    _scene->setSelection(child);
                    _scene->UpdateViews(this, UPDATE_SELECTION);
                    return;
                }
            }
        }

        if (item->IsGroup() && x > CB_SPACING && x < CB_SPACING + CB_WIDTH) {
            // collapse box was picked
            item->SetCollapsed(!item->IsCollapsed());
            RefreshItemList();
            // move all controls below the item toggled
            MoveControls(GetColumnWidth(0));
            swInvalidateWindow(_window);
        } else {
            _trackPoint = Point(x, y);
            _selectedOffset = item->GetFieldOffset(x - width0);
        }
    }
}

// test if a mousedrag would change a value
// if yes, show a cursor different from usual Arrow

void
FieldView::UpdateTrackingCursor(int x, int y)
{
    bool cursorIsArrow = true;
    int hit=HitTest(x, y);
    if (hit != -1)
        if (_items[hit]->IsTrackable()) {
            Rect r;
            GetItemRect(hit, &r);
            int width0 = GetColumnWidth(0);
            if ((x > width0) && (x - width0 < GetItemWidth()))
                cursorIsArrow = false;
        }
    if ((cursorIsArrow) && (!_cursorIsArrow))
        swSetCursor(_window, _cursorArrow);
    if ((!cursorIsArrow) && (_cursorIsArrow))
        swSetCursor(_window, _cursorHMove);     
     _cursorIsArrow = cursorIsArrow;
}

void FieldView::OnMouseMove(int x, int y, int modifiers) 
{
    FieldValue *newValue;

    if (_selectedItem == -1)
        return;
    if (_state == WAIT_EDIT || _state == WAIT_TRACK) {
        if (_items[_selectedItem]->IsTrackable()) {
            StartTracking();
        } else {
            _state = NORMAL;
        }
    }
    UpdateTrackingCursor(x, y);
    if (_state == TRACKING) {
        FieldViewItem  *item = _items[_selectedItem];
        int delta = x - _trackPoint.x;
        newValue = item->OnMouseMove(_trackValue, _selectedOffset, delta);
        ChangeValue(item, newValue);
    }
}

void FieldView::ChangeValue(FieldViewItem *item, FieldValue *newValue)
{
    assert(item && newValue);

    if ((!newValue->equals(item->GetValue())) || 
        (isMFType(newValue->getType()))) {
        int field = _selectedField;
        newValue->clamp(_selectedNode->getProto()->getField(field)->getMin(),
                        _selectedNode->getProto()->getField(field)->getMax());
        int index = _selectedIndex;        
        if (item->GetParent()) {
            MFieldValue *value = (MFieldValue *) _selectedNode->getField(field);
            value->setSFValue(_selectedIndex, newValue);
            item->SetValue(newValue);
            _scene->setField(_selectedNode, field, value);
            swInvalidateWindow(_window);
        } else if (isMFType(newValue->getType())) {             
            // index is 0, field is collapsed
            index = 0;
            MFieldValue *value = (MFieldValue *) _selectedNode->getField(field);
            FieldValue *sfValue = ((MFieldValue *)newValue)->getSFValue(0);
            value->setSFValue(index, sfValue);
            ((MFieldViewItem *)item)->InitIndexValue(0, newValue);
            _scene->setField(_selectedNode, field, value);
            swInvalidateWindow(_window);
        } else {
            _scene->setField(_selectedNode, field, newValue);
            _fields[FindField(field)]->SetValue(newValue);
        }
        FieldUpdate update(_selectedNode, field, index);
        _scene->UpdateViews(this, UPDATE_FIELD, (Hint *) &update);
    } else {
        delete newValue;
    }
}

void FieldView::OnLButtonUp(int x, int y, int modifiers) 
{
    if (_state == TRACKING) {
        StopTracking();
    } else if (_state == WAIT_EDIT) {
        StartEditing();
    } else {
        _state = NORMAL;
    }
}

void FieldView::StartTracking()
{
    Command *cmd;

    _state = TRACKING;
    swSetCapture(_window);
    _trackValue = _items[_selectedItem]->GetValue();
    _trackValue->ref();
    if (_selectedIndex != -1) {
        cmd = new MFieldCommand(_selectedNode, _selectedField, _selectedIndex);
    } else {
        cmd = new FieldCommand(_selectedNode, _selectedField);
    }
    _scene->add(cmd);
}

void FieldView::StopTracking()
{
    _state = NORMAL;
    swReleaseCapture(_window);
    _trackValue->unref();
    _trackValue = NULL;
}

void FieldView::RefreshItemList()
{
    int n = _fields.size();
    int item = 0;

    _items.resize(0);
    for (int i = 0; i < n; i++) {
        _items[item++] = _fields[i];
        if (_fields[i]->IsGroup() && !_fields[i]->IsCollapsed()) {
            MFieldViewItem *mf = (MFieldViewItem *) _fields[i];
            int nc = mf->GetNumChildren();
            for (int j = 0; j < nc; j++) {
                _items[item++] = mf->GetChild(j);
                mf->GetChild(j)->SetIndex(j);
            }
        }
    }
    _height = (_items.size()+1) * _itemHeight;
    int width, height;
    swGetSize(_window, &width, &height);
    swSetScrollSizes(_scroller, _width, _height);
    _pageHeight = height / _itemHeight;
    UpdateBars();
}

void FieldView::DeleteLastSelection(void)
{
    if ((_state == NORMAL) && (_selectedItem > -1)) {
        FieldViewItem *item = _items[_selectedItem];
        MFieldViewItem *parent = (MFieldViewItem *)item->GetParent();
        if (parent) {
            // selected item is a child of an MField; 
            parent->RemoveItem(this, item->GetIndex());
            _selectedField = -1;
            _selectedItem = -1;
//            UpdateAll();
            RefreshItemList();
            Node *node =_scene->getSelection()->getNode();                    
            if (node != NULL)
                node->update();
            // move all controls below the item toggled
            MoveControls(GetColumnWidth(0));
            swInvalidateWindow(_window);
            _scene->UpdateViews(this, UPDATE_SELECTION);
        }
    }
}

/*
void FieldView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
    OnScroll(MAKEWORD(nSBCode, -1), nPos);
}

void FieldView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
    int oldScrollY = _scrollY;

    switch (nSBCode) {
    case SB_TOP:
        _scrollY = 0;
        break;
    case SB_BOTTOM:
        _scrollY = _items.size() - 1 - _pageHeight;
        break;
    case SB_LINEUP:
        _scrollY--;
        break;
    case SB_LINEDOWN:
        _scrollY++;
        break;
    case SB_PAGEUP:
        _scrollY -= _pageHeight;
        break;
    case SB_PAGEDOWN:
        _scrollY += _pageHeight;
        if (_scrollY > _items.size() - 1 - _pageHeight) {
            _scrollY = _items.size() - 1 - _pageHeight;
        }
        break;
    case SB_THUMBTRACK:
    case SB_THUMBPOSITION:
        _scrollY = (int) (nPos * _scrollRatio);
        break;
    }
    _scrollY = CLAMP(_scrollY, 0, _items.size() - _pageHeight + 1);
    if (_scrollY != oldScrollY) {
        Rect r;
        GetClientRect(&r);
        SetScrollPos(SB_VERT, (int) (_scrollY / _scrollRatio));
        r.top = _itemHeight; // don't scroll header
        ScrollWindow(0, (oldScrollY - _scrollY) * _itemHeight, r, r);
        UpdateWindow();
    }
}
*/
