From d9f75e532969bfa3ab00159bd837aadcd580efbd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= <soutade@gmail.com>
Date: Tue, 28 Sep 2010 21:38:14 +0200
Subject: [PATCH] Fix a lot of bugs (bad fix operation count, bad calendar
 display, operation metas not copied) and start deep implementation of meta.
 DON'T USE THIS VERSION. It's a very buggy one !

---
 src/view/AccountPanel.h                |   2 +-
 src/view/SearchPanel.h                 |   2 +-
 src/view/{ => grid}/CalendarEditor.cpp |   2 +-
 src/view/{ => grid}/CalendarEditor.h   |   0
 src/view/grid/GridAccount.cpp          | 367 ++++++++++++++++++++++---
 src/view/grid/GridAccount.h            |   6 +-
 6 files changed, 331 insertions(+), 48 deletions(-)
 rename src/view/{ => grid}/CalendarEditor.cpp (99%)
 rename src/view/{ => grid}/CalendarEditor.h (100%)

diff --git a/src/view/AccountPanel.h b/src/view/AccountPanel.h
index 7e2a150..09d0928 100644
--- a/src/view/AccountPanel.h
+++ b/src/view/AccountPanel.h
@@ -26,7 +26,7 @@
 #include <wx/pie/pieplot.h>
 #include <wx/chartpanel.h>
 #include <wx/scrolwin.h>
-#include "CalendarEditor.h"
+#include "grid/CalendarEditor.h"
 #include "grid/wxGridCellBitmapRenderer.h"
 
 #include "view.h"
diff --git a/src/view/SearchPanel.h b/src/view/SearchPanel.h
index 82557b8..c643284 100644
--- a/src/view/SearchPanel.h
+++ b/src/view/SearchPanel.h
@@ -24,7 +24,7 @@
 #include <wx/grid.h>
 #include <wx/treectrl.h>
 #include <wx/scrolwin.h>
-#include "CalendarEditor.h"
+#include "grid/CalendarEditor.h"
 #include "grid/wxGridCellBitmapRenderer.h"
 #include "AccountPanel.h"
 #include "grid/GridAccount.h"
diff --git a/src/view/CalendarEditor.cpp b/src/view/grid/CalendarEditor.cpp
similarity index 99%
rename from src/view/CalendarEditor.cpp
rename to src/view/grid/CalendarEditor.cpp
index e66818e..a799e5e 100644
--- a/src/view/CalendarEditor.cpp
+++ b/src/view/grid/CalendarEditor.cpp
@@ -24,7 +24,7 @@ CalendarEditor::CalendarEditor(int day, int month, int year) : _day(day), _month
     wxDateTime date;
     int i;
 
-    _maxDay = date.GetLastMonthDay ((wxDateTime::Month) month, year).GetDay()+1;
+    _maxDay = date.GetLastMonthDay ((wxDateTime::Month) month, year).GetDay();
 
     _days = new wxString[_maxDay];
   
diff --git a/src/view/CalendarEditor.h b/src/view/grid/CalendarEditor.h
similarity index 100%
rename from src/view/CalendarEditor.h
rename to src/view/grid/CalendarEditor.h
diff --git a/src/view/grid/GridAccount.cpp b/src/view/grid/GridAccount.cpp
index ad4efb3..0c6e98b 100644
--- a/src/view/grid/GridAccount.cpp
+++ b/src/view/grid/GridAccount.cpp
@@ -143,7 +143,7 @@ void GridAccount::LoadOperations(std::vector<Operation>* operations, bool canAdd
     _curYear = year;
     _displayedOperations.clear();
     _displayedOperations.push_back(NULL); // Header
-    _fixCosts = 1;
+    _fixCosts = 0;
 
     it = _operations->begin();
 
@@ -188,9 +188,6 @@ void GridAccount::InsertOperationWithWeek(User* user, Operation* op, int line, b
 
     InsertOperation(user, op, line, fix, month, year);
 
-    if (fix)
-        _fixCosts++;
-
     if (op && !fix)
     {
         for (it = _displayedOperations.begin(), curLine=0;
@@ -222,7 +219,8 @@ void GridAccount::InsertOperationWithWeek(User* user, Operation* op, int line, b
 void GridAccount::InsertOperation(User* user, Operation* op, int line, bool fix, int month, int year)
 {
     std::vector<Operation>::iterator it;
-    int r, g, b;
+    std::vector<Operation*>::iterator it2;
+    int r, g, b, amount;
     wxColour color;
     wxDateTime curDate;
     wxString description;
@@ -241,7 +239,7 @@ void GridAccount::InsertOperation(User* user, Operation* op, int line, bool fix,
 
     if (op && op->meta)
     {
-	SetCellRenderer(line, TREE, new wxGridCellTreeButtonRenderer());
+	SetCellRenderer(line, TREE, new wxGridCellTreeButtonRenderer(true));
 	SetCellEditor(line, TREE, new wxGridCellTreeButtonEditor());
 
         SetReadOnly(line, DATE, true);
@@ -264,6 +262,7 @@ void GridAccount::InsertOperation(User* user, Operation* op, int line, bool fix,
     {
         SetCellValue(line, CATEGORY, _("Fix"));
         SetReadOnly(line, CATEGORY);
+	_fixCosts++;
     }
 
     if (op)
@@ -279,8 +278,9 @@ void GridAccount::InsertOperation(User* user, Operation* op, int line, bool fix,
             SetCellValue(line, DEBIT, wxString::Format(wxT("%.2lf"), -op->amount));
         else
             SetCellValue(line, CREDIT, wxString::Format(wxT("%.2lf"), op->amount));
-        SetCellValue(line, ACCOUNT, user->GetAccountName(op->account));
-        if (!fix)
+	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 ());
@@ -304,6 +304,19 @@ void GridAccount::InsertOperation(User* user, Operation* op, int line, bool fix,
             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
     {
@@ -385,6 +398,80 @@ void GridAccount::DeleteOperation(const Operation& op)
 	}
 }
 
+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 (!(_displayedOperations)[i]->fix_cost && op->fix_cost)
+	i --;
+    else if (i == (int)_displayedOperations.size() ||
+	i == _fixCosts) 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<Operation*>::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);
+		_displayedOperations.erase(_displayedOperations.begin()+line+1);
+		if (deleteOp)
+		{
+		    DeleteOperation(**it);
+		    _kiss->DeleteOperation(**it);
+		}
+	    }
+		
+	}
+    }
+
+    if (removeRoot)
+    {
+	DeleteRows(line, 1);
+	_displayedOperations.erase(_displayedOperations.begin()+line);
+	if (deleteOp)
+	{
+	    DeleteOperation(*op);
+	    _kiss->DeleteOperation(*op);
+	}
+    }
+
+    treeRenderer->DecRef();
+}
+
 void GridAccount::OnOperationModified(wxGridEvent& event)
 {
     User* user = _kiss->GetUser();
@@ -398,11 +485,39 @@ void GridAccount::OnOperationModified(wxGridEvent& event)
     static bool inModification = false ;
     wxColour color ;
     unsigned char r, g, b;
+    wxGridCellTreeButtonRenderer* treeRenderer;
+    std::vector<Operation*>::iterator it;
+    Operation* op;
 
     // Avoid recursives calls
     if (inModification) return;
 
     inModification = true ;
+    
+    if (col == TREE)
+    {
+	treeRenderer = (wxGridCellTreeButtonRenderer*) GetCellRenderer(row, col);
+	
+	op = _displayedOperations[row];
+
+	// Invert not already applied
+	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(""));
@@ -510,10 +625,18 @@ void GridAccount::OnOperationModified(wxGridEvent& event)
 
         if (col == DELETE)
 	{
-            DeleteRows(row, 1);
-	    DeleteOperation(cur_op);
-	    _kiss->DeleteOperation(cur_op);
-            _displayedOperations.erase(_displayedOperations.begin()+row);
+	    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();
@@ -523,6 +646,9 @@ void GridAccount::OnOperationModified(wxGridEvent& event)
         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)
 	{
@@ -551,6 +677,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event)
         need_insertion = true;
         fix_op = true;
         new_op.fix_cost = true;
+	new_op.meta = false;
 
         for(i=0; i<NUMBER_COLS_OPS; i++)
 	{
@@ -560,7 +687,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event)
 
 	DEFAULT_FONT(font);
 
-	SET_ROW_COLOR(row, OWN_GREEN, *wxBLACK);
+	SET_ROW_COLOR(row, OWN_YELLOW, *wxBLACK);
 	SET_ROW_FONT(row, font);
 
         new_op.id = _kiss->AddOperation(new_op);
@@ -572,13 +699,24 @@ void GridAccount::OnOperationModified(wxGridEvent& event)
         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)
 	{
-            DeleteRows(row, 1);
-	    DeleteOperation(cur_op);
-            _displayedOperations.erase(_displayedOperations.begin()+row);
-            _kiss->DeleteOperation(cur_op);
+	    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 ;
@@ -608,6 +746,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event)
         need_insertion = true;
         fix_op = false;
         new_op.fix_cost = false;
+	new_op.meta = false;
 
         for(i=0; i<NUMBER_COLS_OPS; i++)
 	{
@@ -623,41 +762,181 @@ void GridAccount::OnOperationModified(wxGridEvent& event)
     }
 
     if (need_insertion)
-    {
-        for(i=0; i<(int)_displayedOperations.size(); i++)
-	{
-	    if (_displayedOperations[i] == NULL) continue;
-            if ((_displayedOperations)[i]->fix_cost && !fix_op) continue;
-            if (!(_displayedOperations)[i]->fix_cost && fix_op) break;
-            if (user->_preferences[wxT("operation_order")] == wxT("ASC"))
-	    {
-                if ((_displayedOperations)[i]->day > new_op.day)
-                    break;
-
-	    }
-            else
-	    {
-                if ((_displayedOperations)[i]->day < new_op.day)
-                    break;
-	    }
-	}
-
-	if (i == (int)_displayedOperations.size()) i--;
-
-        _operations->push_back(new_op);
-
-        InsertOperationWithWeek(user, &((*_operations)[_operations->size()-1]), i, fix_op, _curMonth, _curYear);
-        if (fix_op)
-            _fixCosts = _fixCosts+1;
-    }
+	InsertIntoGrid(&new_op);
 
     inModification = false ;
     event.Skip();
 }
 
+void GridAccount::UpdateMeta(Operation* op, std::vector<Operation*> ops)
+{
+    std::vector<Operation*>::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;
+	}
+    }
+
+    if (updateCat)
+	op->category = category;
+}
+
 void GridAccount::Group()
 {
+    std::vector<int> rows;
+    std::vector<int>::iterator it;
+    std::vector<Operation*> ops;
+    std::vector<Operation*>::iterator it2, it3;
+    wxString parent = wxT("");
+    Operation* op;
+    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;
+		else if(op->meta)
+		    parent = op->id;
+	    }
+	    else
+	    {
+		if ((parent.Length() && op->parent != parent) || (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;
+
+	    rows.push_back(c.GetRow());
+	    ops.push_back(op);
+	}
+    }
+    
+    if (rows.size() < 2) return;
+
+    if (!parent.Length())
+    {
+	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
+    {
+	for(i=0, it2=ops.begin(); it2!=ops.end(); it2++, i++)
+	    if ((*it2)->id == parent)
+		break;
+	op = *it2;
+    }
+
+    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()
diff --git a/src/view/grid/GridAccount.h b/src/view/grid/GridAccount.h
index e5c9d82..5f9cd68 100644
--- a/src/view/grid/GridAccount.h
+++ b/src/view/grid/GridAccount.h
@@ -23,6 +23,7 @@
 #include <wx/wx.h>
 #include <wx/grid.h>
 #include <list>
+#include <algorithm>
 #include <view/AccountPanel.h>
 #include <model/model.h>
 #include <controller/KissCount.h>
@@ -65,8 +66,11 @@ private:
 
     void SetWeek(int week, int line);
     void ResetWeeks();
+    void InsertIntoGrid(Operation* op);
     void DeleteOperation(const Operation& op);
-
+    void UpdateMeta(Operation* op, std::vector<Operation*> ops);
+    void RemoveMeta(Operation* op, int line, bool removeRoot, bool deleteOp);
+    
     DECLARE_EVENT_TABLE();
 };
 #endif