/*
  Copyright 2010 Grégory Soutadé
  This file is part of KissCount.
  KissCount 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 3 of the License, or
  (at your option) any later version.
  KissCount 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 KissCount.  If not, see .
*/
#include "GridAccount.h"
#define SET_ROW_COLOR(row, backcolor, forecolor)  for(int i=0; iGetUser();
    std::vector::iterator accountIt;
    std::vector::iterator categoryIt;
    CreateGrid(1, NUMBER_COLS_OPS);
    SetColLabelSize(0);
    SetRowLabelSize(0);
    SetColSize (DESCRIPTION, GetColSize(DESCRIPTION)*3);
    SetDefaultCellFont(font);
    font.SetWeight(wxFONTWEIGHT_BOLD);
    wxString colsName[] = {wxT(""), _("Description"), _("Date"), _("Debit"), _("Credit"), _("Category"), _("Account"), wxT(""), wxT("")};
    for(i=0; iGetAccountsNumber()];
    for (i=0, 
             accountIt = user->_accounts.begin();
         accountIt != user->_accounts.end();
         accountIt++, i++)
        _accounts[i] = accountIt->name;
    _categories = new wxString[user->GetCategoriesNumber()] ;
    for(i=0, categoryIt = user->_categories.begin(); 
        categoryIt != user->_categories.end(); 
        categoryIt++, i++)
    {
        _categories[i] = categoryIt->name ;
    }
    Connect(id, wxEVT_GRID_CELL_CHANGE, wxGridEventHandler(GridAccount::OnOperationModified), NULL, this);
    AutoSizeColumn(TREE, false);
    AutoSizeColumn(CATEGORY, false);
    AutoSizeColumn(DATE, false);
    AutoSizeColumn(ACCOUNT, false);
    AutoSizeColumn(DELETE, false);
    AutoSizeColumn(CHECKED, false);
}
GridAccount::~GridAccount()
{
    delete[] _categories;
    delete[] _accounts;
}
wxPen GridAccount::GetColGridLinePen (int col) 
{return wxPen(*wxBLACK, 1, wxSOLID);} 
wxPen GridAccount::GetRowGridLinePen (int row) {
    if (row == 0 || row == _fixCosts ||
        row == _week1 ||
        row == _week2 ||
        row == _week3 ||
        row == _week4)
        return wxPen(*wxBLACK, 1, wxSOLID);
    
    return GetCellBackgroundColour(row, 0);
} 
void GridAccount::ResetWeeks()
{
    _week1 = _week2 = _week3 = _week4 = 0;
}
void GridAccount::SetWeek(int week, int line) {
    switch (week) {
    case 1: _week1 = line; break;
    case 2: _week2 = line; break;
    case 3: _week3 = line; break;
    case 4: _week4 = line; break;
    }
}
void GridAccount::LoadOperations(std::vector* operations, bool canAddOperation, bool setWeek, int month, int year)
{
    std::vector::iterator it;
    User* user = _kiss->GetUser();
    int curLine = 0;
    _operations = operations;
    _canAddOperation = canAddOperation;
    _curMonth = month;
    _curYear = year;
    _displayedOperations.clear();
    _displayedOperations.push_back(NULL); // Header
    _fixCosts = 0;
    it = _operations->begin();
    for (;it != _operations->end() && it->fix_cost; it++)
    {
	if (it->parent.Length()) continue;
	if (setWeek)
	    InsertOperationWithWeek(user, &(*it), ++curLine, true, it->month, it->year);
	else
	    InsertOperation(user, &(*it), ++curLine, true, it->month, it->year);
    }
    if (canAddOperation)
	InsertOperation(user, NULL, ++curLine, true, month, year);
    for (; it != _operations->end(); it++)
    {
	if (it->parent.Length()) continue;
	if (setWeek)
	    InsertOperationWithWeek(user, &(*it), ++curLine, false, it->month, it->year);
	else
	    InsertOperation(user, &(*it), ++curLine, false, it->month, it->year);
    }
    if (canAddOperation)
	InsertOperation(user, NULL, ++curLine, false, month, year);
    AutoSizeColumn(TREE, false);
    AutoSizeColumn(CATEGORY, false);
    AutoSizeColumn(DATE, false);
    AutoSizeColumn(ACCOUNT, false);
    AutoSizeColumn(DELETE, false);
    AutoSizeColumn(CHECKED, false);
}
void GridAccount::InsertOperationWithWeek(User* user, Operation* op, int line, bool fix, int month, int year)
{
    std::vector::iterator it;
    int curLine, curWeek, week, i;
    InsertOperation(user, op, line, fix, month, year);
    if (op && !fix)
    {
        for (it = _displayedOperations.begin(), curLine=0;
             it != _displayedOperations.end(); 
             it++, curLine++)
	{
	    if (*it && !(*it)->fix_cost) break;
	}
        if (it == _displayedOperations.end()) return;
	ResetWeeks();
        curWeek = wxDateTime((*it)->day+1, (wxDateTime::Month)(*it)->month, (*it)->year).GetWeekOfMonth();
	it++;
        for (i=1; it != _displayedOperations.end(); it++, curLine++)
	{
	    if (!*it) continue;
            week = wxDateTime((*it)->day+1, (wxDateTime::Month)(*it)->month, (*it)->year).GetWeekOfMonth();
            if (week != curWeek)
	    {
                SetWeek(i++, curLine);
                curWeek = week;
	    }
	}
    }
}
void GridAccount::InsertOperation(User* user, Operation* op, int line, bool fix, int month, int year)
{
    std::vector::iterator it;
    std::vector::iterator it2;
    int r, g, b, amount;
    wxColour color;
    wxDateTime curDate;
    wxString description;
    wxFont font;
    Category cat ;
    curDate.SetToCurrent();
    // // First is header
    // if (op)
    _displayedOperations.insert(_displayedOperations.begin()+line, op);
    if (!user->_accounts.size()) return;
    InsertRows(line, 1);
    if (op && op->meta)
    {
	SetCellRenderer(line, TREE, new wxGridCellTreeButtonRenderer());
	SetCellEditor(line, TREE, new wxGridCellTreeButtonEditor());
        SetReadOnly(line, DATE, true);
        SetReadOnly(line, CREDIT, true);
        SetReadOnly(line, DEBIT, true);
        SetReadOnly(line, CATEGORY, true);
        SetReadOnly(line, ACCOUNT, true);
    }
    else
        SetReadOnly(line, TREE, true);
    SetCellEditor(line, DEBIT, new wxGridCellFloatEditor(wxID_ANY, 2));
    SetCellEditor(line, CREDIT, new wxGridCellFloatEditor(wxID_ANY, 2));
    wxGridCellChoiceEditor* accountEditor = new wxGridCellChoiceEditor(user->GetAccountsNumber(), _accounts, false);
    SetCellEditor(line, ACCOUNT, accountEditor);
    wxGridCellChoiceEditor* categoryEditor = new wxGridCellChoiceEditor(user->GetCategoriesNumber()-1, _categories+1, false);
    SetCellEditor(line, CATEGORY, categoryEditor);
    if (fix)
    {
        SetCellValue(line, CATEGORY, _("Fix"));
        SetReadOnly(line, CATEGORY);
	_fixCosts++;
    }
    if (op)
    {
        cat = user->GetCategory(op->category);
        SetCellEditor(line, DATE, new CalendarEditor(op->day, month, year));    
        description = op->description;
        UNESCAPE_CHARS(description);
        SetCellValue(line, DESCRIPTION, description);
        SetCellValue(line, DATE, wxString::Format(wxT("%02d/%02d/%d"), op->day+1, month+1, year));
        if (op->amount < 0)
            SetCellValue(line, DEBIT, wxString::Format(wxT("%.2lf"), -op->amount));
        else
            SetCellValue(line, CREDIT, wxString::Format(wxT("%.2lf"), op->amount));
	if (!op->meta)
	    SetCellValue(line, ACCOUNT, user->GetAccountName(op->account));
        if (!fix && !op->meta)
            SetCellValue(line, CATEGORY, cat.name);
        SetCellRenderer(line, DELETE, new wxGridCellBoolRenderer ());
        SetCellEditor(line, DELETE, new wxGridCellBoolEditor ());
        SetCellRenderer(line, CHECKED, new wxGridCellBoolRenderer ());
        SetCellEditor(line, CHECKED, new wxGridCellFastBoolEditor ());
        color = cat.backcolor;
        if (op->checked)
	{
            r = ((color.Red()*1.5) >= 0xFF) ? 0xFF : color.Red()*1.5 ; 
            g = ((color.Green()*1.5) >= 0xFF) ? 0xFF : color.Green()*1.5 ; 
            b = ((color.Blue()*1.5) >= 0xFF) ? 0xFF : color.Blue()*1.5 ; 
            color.Set(r, g, b, color.Alpha());
            SetCellValue(line, CHECKED, wxT("1"));
	}
        SET_ROW_COLOR(line, color, cat.forecolor);
        if (cat.font.Length())
        {
            font = user->GetCategoryFont(cat.id);
            SET_ROW_FONT(line, font);
        }
	if (op->meta && !op->amount)
	{
	    amount = 0;
	    for(it2=op->childs.begin(); it2!=op->childs.end(); it2++)
	    {
		if ((*it2)->amount > 0)
		    amount += (*it2)->amount;
	    }
            SetCellValue(line, DEBIT, wxString::Format(wxT("%.2lf"), amount));
            SetCellValue(line, CREDIT, wxString::Format(wxT("%.2lf"), amount));
	}
    }
    else
    {
        SetCellEditor(line, DATE, new CalendarEditor(0, month, year));
        if (!fix &&
            curDate.GetMonth() == month &&
            curDate.GetYear() == year)
	{
            SetCellValue(line, DATE, wxString::Format(wxT("%02d/%02d/%d"), curDate.GetDay(), month+1, year));
            SetCellEditor(line, DATE, new CalendarEditor(curDate.GetDay()-1, month, year));
	}
        else
            SetCellEditor(line, DATE, new CalendarEditor(0, month, year));
	
        if (fix)
        {
            SET_ROW_COLOR(line, OWN_YELLOW, *wxBLACK);
        }
        else
        {
            SET_ROW_COLOR(line, OWN_GREEN, *wxBLACK);
        }
        SetReadOnly(line, CHECKED, true);
        SetReadOnly(line, DELETE, true);
    }
    
    SetCellAlignment(line, DATE, wxALIGN_CENTRE, wxALIGN_CENTRE);
    SetCellAlignment(line, DEBIT, wxALIGN_RIGHT, wxALIGN_CENTRE);
    SetCellAlignment(line, CREDIT, wxALIGN_RIGHT, wxALIGN_CENTRE);
    SetCellAlignment(line, DELETE, wxALIGN_CENTRE, wxALIGN_CENTRE);
    SetCellAlignment(line, CHECKED, wxALIGN_CENTRE, wxALIGN_CENTRE);
    AutoSizeRow(line);
    Layout();
    SetMinSize(GetMinSize());
}
// From http://nomadsync.cvs.sourceforge.net/nomadsync/nomadsync/src/
void GridAccount::OnCellLeftClick(wxGridEvent& evt)
{
    if (evt.GetCol() != TREE && evt.GetCol() != DELETE && evt.GetCol() != CHECKED) { evt.Skip() ; return;}
    // This forces the cell to go into edit mode directly
    //m_waitForSlowClick = TRUE;
    SetGridCursor(evt.GetRow(), evt.GetCol());
    // Store the click co-ordinates in the editor if possible
    // if an editor has created a ClientData area, we presume it's
    // a wxPoint and we store the click co-ordinates
    wxGridCellEditor* pEditor = GetCellEditor(evt.GetRow(), evt.GetCol());
    wxPoint* pClickPoint = (wxPoint*)pEditor->GetClientData();
    if (pClickPoint)
    {
 	*pClickPoint = ClientToScreen(evt.GetPosition());
#ifndef __WINDOWS__
 	EnableCellEditControl(true);
#endif
    }
    // hack to prevent selection from being lost when click combobox
    if (/*evt.GetCol() == 0 && */IsInSelection(evt.GetRow(), evt.GetCol()))
    {
 	//m_selTemp = m_selection;
 	m_selection = NULL;
    }
    pEditor->DecRef();
    evt.Skip();
} 
void GridAccount::DeleteOperation(const Operation& op)
{
    std::vector::iterator it;
    for (it=_operations->begin(); it!=_operations->end(); it++)
	if (it->id == op.id)
	{
	    _operations->erase(it);
	    break;
	}
}
void GridAccount::InsertIntoGrid(Operation* op)
{
    int i;
    User* user = _kiss->GetUser();
    for(i=0; i<(int)_displayedOperations.size(); i++)
    {
	if (_displayedOperations[i] == NULL) continue;
	if ((_displayedOperations)[i]->fix_cost && !op->fix_cost) continue;
	if (!(_displayedOperations)[i]->fix_cost && op->fix_cost) break;
	if (user->_preferences[wxT("operation_order")] == wxT("ASC"))
	{
	    if ((_displayedOperations)[i]->day > op->day)
		break;
	}
	else
	{
	    if ((_displayedOperations)[i]->day < op->day)
		break;
	}
    }
    if (i == (int)_displayedOperations.size() ||
	i == _fixCosts) 
	i--;
    else if (!(_displayedOperations)[i]->fix_cost && op->fix_cost)
	i --;
    _operations->push_back(*op);
    InsertOperationWithWeek(user, &((*_operations)[_operations->size()-1]), i, op->fix_cost, _curMonth, _curYear);
}
void GridAccount::RemoveMeta(Operation* op, int line, bool removeRoot, bool deleteOp)
{
    std::vector::iterator it, it2;
    wxGridCellTreeButtonRenderer* treeRenderer;
    treeRenderer = (wxGridCellTreeButtonRenderer*) GetCellRenderer(line, TREE);
    if (treeRenderer->IsCollapsed())
    {
	for(it=op->childs.begin(); it!=op->childs.end(); it++)
	{
	    if ((*it)->meta)
		RemoveMeta(*it, line+1, true, deleteOp);
	    else
	    {
		DeleteRows(line+1, 1);
		if (_displayedOperations[line+1]->fix_cost) _fixCosts--;
		_displayedOperations.erase(_displayedOperations.begin()+line+1);
		if (deleteOp)
		{
		    DeleteOperation(**it);
		    _kiss->DeleteOperation(**it);
		}
	    }
		
	}
    }
    if (removeRoot)
    {
	DeleteRows(line, 1);
	if (_displayedOperations[line]->fix_cost) _fixCosts--;
	_displayedOperations.erase(_displayedOperations.begin()+line);
	if (deleteOp)
	{
	    DeleteOperation(*op);
	    _kiss->DeleteOperation(*op);
	}
    }
    treeRenderer->DecRef();
}
void GridAccount::OnOperationModified(wxGridEvent& event)
{
    User* user = _kiss->GetUser();
    int row = event.GetRow();
    int col = event.GetCol();
    Operation new_op, cur_op, op_tmp;
    int op_complete = 6, i;
    wxString value ;
    wxDateTime date;
    bool need_insertion = false, fix_op=false;
    static bool inModification = false ;
    wxColour color ;
    unsigned char r, g, b;
    wxGridCellTreeButtonRenderer* treeRenderer;
    std::vector::iterator it;
    Operation* op;
    // Avoid recursives calls
    if (inModification) return;
    inModification = true ;
    
    if (col == TREE)
    {
	treeRenderer = (wxGridCellTreeButtonRenderer*) GetCellRenderer(row, col);
	
	op = _displayedOperations[row];
	if (!treeRenderer->IsCollapsed())
	{
	    for (i=1, it=op->childs.begin(); it!=op->childs.end(); it++, i++)
	    {
		InsertOperation(user, *it, row+i, (*it)->fix_cost, _curMonth, _curYear);
	    }
	}
	else
	{
	    RemoveMeta(op, row, false, false);
	}
	treeRenderer->DecRef();
	inModification = false;
	return;
    }
    if (col == DEBIT)
        SetCellValue(row, CREDIT, wxT(""));
    else if (col == CREDIT)
        SetCellValue(row, DEBIT, wxT(""));
    value = GetCellValue(row, DESCRIPTION);
    if (value.Length())
    {
        new_op.description = value;
        op_complete--;
    }
    value = GetCellValue(row, DATE);
    if (value.Length())
    {
        date.ParseFormat(value, wxT("%d/%m/%Y"));
        new_op.day = date.GetDay()-1;
        new_op.month = date.GetMonth();
        new_op.year = date.GetYear();
        op_complete--;
    }
  
    value = GetCellValue(row, DEBIT);
    if (value.Length())
    {
        value.ToDouble(&new_op.amount);
        new_op.amount *= -1.0;
        op_complete--;
    }
    value = GetCellValue(row, CREDIT);
    if (value.Length())
    {
        value.ToDouble(&new_op.amount);
        op_complete--;
    }
    value = GetCellValue(row, CATEGORY);
    if (value.Length())
    {
        new_op.category = user->GetCategoryId(value);
        op_complete--;
    }
    value = GetCellValue(row, ACCOUNT);
    if (value.Length())
    {
        new_op.account = user->GetAccountId(value);
        op_complete--;
    }
    value = GetCellValue(row, CHECKED);
    if (value.Length() && value != wxT("0"))
        new_op.checked = true;
    else
        new_op.checked = false;
    op_complete--;
    if (col == DESCRIPTION &&
        (!GetCellValue(row, CATEGORY).Length() ||
         !GetCellValue(row, ACCOUNT).Length()))
    {
        if (_kiss->SearchPreviousOperation(&op_tmp, new_op.description, _curMonth, _curYear))
	{
            new_op.category = op_tmp.category;
            new_op.account = op_tmp.account;
            SetCellValue(row, CATEGORY, user->GetCategoryName(new_op.category));
            SetCellValue(row, ACCOUNT, user->GetAccountName(new_op.account));
            op_complete -= 2;
	}
    }
    if (col == CHECKED || col == CATEGORY)
    {
        color = user->GetCategory(new_op.category).backcolor;
        if (new_op.checked)
	{
            r = ((color.Red()*1.5) >= 0xFF) ? 0xFF : color.Red()*1.5 ; 
            g = ((color.Green()*1.5) >= 0xFF) ? 0xFF : color.Green()*1.5 ; 
            b = ((color.Blue()*1.5) >= 0xFF) ? 0xFF : color.Blue()*1.5 ; 
            color.Set(r, g, b, color.Alpha());
	}
        SET_ROW_COLOR(row, color, user->GetCategory(new_op.category).forecolor);
	SET_ROW_FONT(row, user->GetCategoryFont(new_op.category));
    }
    if (col == DELETE)
    {
        wxMessageDialog dialog(this, _("Are you sure want to delete : \n")+new_op.description, wxT("KissCount"), wxYES_NO);
        if (dialog.ShowModal() == wxID_NO)
	{
            SetCellValue(row, col, wxT("0"));
            inModification = false;
            return;
	}
    }
    // Modify a fix operation
    if (row < _fixCosts)
    {
        cur_op = *(_displayedOperations)[row] ;
        if (col == DELETE)
	{
	    if (cur_op.parent.Length())
		user->UnGroup(_displayedOperations[row]);
	    if (cur_op.meta)
		RemoveMeta(_displayedOperations[row], row, true, true);
	    else
	    {
		DeleteRows(row, 1);
		DeleteOperation(cur_op);
		_kiss->DeleteOperation(cur_op);
		_displayedOperations.erase(_displayedOperations.begin()+row);
	    }
            _fixCosts = _fixCosts--;
            inModification = false ;
	    event.Skip();
            return ;
	}
        new_op.id = cur_op.id;
        new_op.fix_cost = true;
        new_op.transfert = cur_op.transfert;
	new_op.meta = cur_op.meta;
	new_op.parent = cur_op.parent;
	new_op.childs = cur_op.childs;
        if (cur_op.day != new_op.day)
	{
            need_insertion = true;
            DeleteRows(row, 1);
	    DeleteOperation(cur_op);
            _displayedOperations.erase(_displayedOperations.begin()+row);
            _fixCosts--;
            _kiss->UpdateOperation(new_op);
	}
        else
	{
            _kiss->UpdateOperation(new_op);
            *(_displayedOperations)[row] = new_op;
	}
        fix_op = true;
    }
    // Add a fixCost
    else if (row == _fixCosts)
    {
        if (op_complete) {
            inModification = false ;
            return ;
        }
        need_insertion = true;
        fix_op = true;
        new_op.fix_cost = true;
	new_op.meta = false;
        for(i=0; iAddOperation(new_op);
    }
    // Modify an operation
    else if (row < (int)(_displayedOperations.size()-1))
    {
        cur_op = *(_displayedOperations)[row] ;
        new_op.id = cur_op.id;
        new_op.fix_cost = false;
        new_op.transfert = cur_op.transfert;
	new_op.meta = cur_op.meta;
	new_op.parent = cur_op.parent;
	new_op.childs = cur_op.childs;
        if (col == DELETE)
	{
	    if (cur_op.parent.Length())
		user->UnGroup(_displayedOperations[row]);
	    if (cur_op.meta)
		RemoveMeta(_displayedOperations[row], row, true, true);
	    else
	    {
		DeleteRows(row, 1);
		DeleteOperation(cur_op);
		_displayedOperations.erase(_displayedOperations.begin()+row);
		_kiss->DeleteOperation(cur_op);
	    }
            inModification = false ;
	    event.Skip();
            return ;
	}
        if (cur_op.day != new_op.day)
	{
            need_insertion = true;
            DeleteRows(row, 1);
	    DeleteOperation(cur_op);
            _displayedOperations.erase(_displayedOperations.begin()+row);
            _kiss->UpdateOperation(new_op);
	}
        else
	{
            _kiss->UpdateOperation(new_op);
            *(_displayedOperations)[row] = new_op;
	}
    }
    // Add an operation
    else
    {
        if (op_complete) {
            inModification = false ;
            return ;
        }
        need_insertion = true;
        fix_op = false;
        new_op.fix_cost = false;
	new_op.meta = false;
        for(i=0; iAddOperation(new_op);
    }
    if (need_insertion)
	InsertIntoGrid(&new_op);
    inModification = false ;
    event.Skip();
}
void GridAccount::UpdateMeta(Operation* op, std::vector& ops)
{
    std::vector::iterator it;
    Operation* op_ ;
    wxString category = wxT("");
    bool updateCat = false ;
    op->category = wxT("");
    for(it=ops.begin(); it!=ops.end(); it++)
    {
	op_ = *it;
	if (op_->year <= op->year && op_->month <= op->month && op_->day < op->day)
	{
	    op->year = op_->year;
	    op->month = op_->month;
	    op->day = op_->day;
	}
	op->amount += op_->amount;
	if (!op->description.Length() && op_->description.Length())
	    op->description = op_->description;
	if (!category.Length())
	{
	    if (op_->category.Length())
	    {
		category = op_->category;
		updateCat = true;
	    }
	}
	else
	{
	    if (op_->category.Length() && op_->category != category)
		updateCat = false;
	}
	op_->parent = op->id;
    }
    if (updateCat)
	op->category = category;
}
void GridAccount::Group()
{
    std::vector rows;
    std::vector::iterator it;
    std::vector ops;
    std::vector::iterator it2, it3;
    wxString parent = wxT("");
    Operation* op=NULL;
    int fix = -1, i;
    // Singly selected cells.
    const wxGridCellCoordsArray& cells(GetSelectedCells());
    for (size_t i = 0; i < cells.size(); ++i)
    {
    	const wxGridCellCoords& c = cells[i];
	for (it=rows.begin(); it!=rows.end(); it++)
	    if (*it == c.GetRow())
		break;
	if (it != rows.end()) continue;
	op = _displayedOperations[c.GetRow()] ;
	if (op)
	{
	    if (!parent.Length())
	    {
		if (op->parent.Length())
		{
		    parent = op->parent;
		    continue;
		}
		else if(op->meta)
		    parent = op->id;
	    }
	    else
	    {
		if ((parent.Length() && op->parent.Length() && op->parent != parent))
		{
		    wxMessageBox(_("Cannot group these operations"), _("Error"), wxICON_ERROR | wxOK);
		    return ;
		}
	    }
	    if (fix != -1 && ((!fix && op->fix_cost) || (fix && !op->fix_cost)))
	    {
		wxMessageBox(_("Cannot group these operations"), _("Error"), wxICON_ERROR | wxOK);
		return ;
	    }
	    if (fix == -1)
		fix = op->fix_cost ? 1 : 0;
	    
	    ops.push_back(op);
	    rows.push_back(c.GetRow());
	}
    }
    
    if (!parent.Length())
    {
	if (rows.size() < 2) return;
	op = new Operation;
	op->parent = wxT("");
	op->day = ops[0]->day;
	op->month = ops[0]->month;
	op->year = ops[0]->year;
	op->amount = 0;
	op->description = wxT("");
	op->category = wxT("");
	op->fix_cost = fix;
	op->account = wxT("");
	op->checked = false;
	op->transfert = wxT("");
	op->formula = wxT("");
	op->meta = true;
        op->id = _kiss->AddOperation(*op);
    }
    else
    {
	if (rows.size() < 1) return;
	for(i=0, it2=_displayedOperations.begin(); it2!=_displayedOperations.end(); it2++, i++)
	    if (*it2 && (*it2)->id == parent)
	    {
		RemoveMeta(*it2, i, true, false);
		op = *it2;
		break;
	    }
	if (!op) return;
    }
    std::sort(rows.begin(), rows.end());
    for(i=0; i<(int)rows.size(); i++)
    {
	if (rows[i] >= i)
	{
	    if (ops[i]->meta)
		RemoveMeta(ops[i], rows[i]-i, true, false);
	    else
	    {
		DeleteRows(rows[i]-i, 1);
		_displayedOperations.erase(_displayedOperations.begin()+rows[i]-i);
	    }
	}
	else
	{
	    if (ops[i]->meta)
		RemoveMeta(ops[i], rows[i], true, false);
	    else
	    {
		DeleteRows(rows[i], 1);
		_displayedOperations.erase(_displayedOperations.begin()+rows[i]);
	    }
	}
    }
    UpdateMeta(op, ops);
    for(it2=ops.begin(); it2!=ops.end(); it2++)
    {
	for (i=0, it3=op->childs.begin(); it3!=op->childs.end(); it3++, i++)
	    if ((*it3)->id == (*it2)->id ||
		(*it3)->day > (*it2)->day)
		break;
	if (i) i--;
	if (it3 == op->childs.end())
	    op->childs.push_back(*it2);
	else if ((*it3)->id == (*it2)->id) 
	    continue;
	else
	    op->childs.insert(op->childs.begin()+i, *it2);
	(*it2)->parent = op->id;
	_kiss->UpdateOperation(**it2);
    }
    _kiss->UpdateOperation(*op);
    InsertIntoGrid(op);
}
void GridAccount::UnGroup()
{
}