Introduction
There are many financial administration programs on the web. But they lack the functionality I need. Shortcuts, fast filter, hierarchical account structure, speed. So I ended up writing my own. It may seem like rewriting the wheel (yet another finance manager program). On the other hand, it was an opportunity to learn and I already had most of the components to build it.
The Program
The utility is an MFC program that uses the feature pack. It also uses a lot of controls from CodeProject. It stores data in a SQLite database. It is compiled so that it works starting with Windows 2000 (no dependencies that I know). It can be modified to work with Win9x by changing the version with PE Explorer, adding GDIPlus.dll and some functions in the code.
It has its own help, but it's not in English (yet).
I have disabled some functionality:
- Get/upload to/from the web: I have a web page where I can access an SQL Server database. I cannot write in the web server. So, I can save and retrieve the data vía HTTP. All the database is encrypted and put in a text field.
- Password/Security: The utility has a very basic protection by using a password. You can enable/disable it in BasicAdmin.cpp.
Structure
The code was written directly. It was not well planned. I added functionality as I needed. This code is not supposed to be a tutorial. It wasn't supposed to be an article. I post it because I used a lot of code from CodeProject. The general structure of the code is:
I modified the original code to upload it in the article. The SQLite and Crypto++ go into separate DLLs in the original code. That is to speed up full compilation.
Also, code in the "Graph" directory are a subset of a larger project (also called Graph) that static links with this project, that is also to speed up compilation.
It compiles OK with warnings in VS 2008.
Functionality
This project implements a lot of functionality, usually required in many projects:
- Fast search
- Add, modify, delete
- Print
- Tree management
- Screen resize
- Database management
- Encryption (security)
- Help
On the other hand, that functionality is not implemented with correctness in mind. It was implemented having in mind time (I don't have much) and features.
Design and Ideas
Explorer View
I always liked the way explorer works. A tree pane in the left and a view pane in the right. The reasons are simple:
- People are used to the way explorer works
- It allows to organize lots of items
- It is not very complicated
There are many other programs that work that way: Visual Studio, SQL Server manager and others to name a few. So, I replicated that functionality (kind of) here. If you click a folder in the tree, you will see a list of the children items in the right pane until you reach an item with no children.
Single Click Access
I don't like functionality that is "buried" inside screens. For example, to go to screen4. Screen1 calls Screen2 calls Screen3 calls Screen4. So, if possible, every screen will be accessible from the tree, directly.
Screen Usage
The data view (grid) should use all the available space. After all, the data view is one of the screens you use the most. So, it should resize with the application. Its columns should resize too. The grid should show all the data loaded.
Grid and Filter
The grid should be fast. It should filter data fast. The problem is that, when you want to filter, you have to go to the database. But, why go to the database if you had to load all the data? So, you can filter the data directly in the grid (putting invisible rows that do not match the filter). When you add an item, you can also add it to the list (no requery the database). That, while it complicates the code a bit, can greatly improve performance and responsiveness.
Graphics
I really like the way Office 2007 looks. And VS 2008 offers the Feature Pack. So, why not use it? To keep the colors consistent, I had to add code for the background, buttons, grid, etc.
Speed
I usually don't like entering data. When I need data, it should be easy to get. So, the utility features:
- Minimized mode in the system tray
- Fast filter (again)
- Shortcuts
- Few fields (columns)
The Code
There are several parts of the code that can be useful to solve different problems. For example:
Color the Background and Avoid Gray Static Controls
BEGIN_MESSAGE_MAP(CFrmCashManTotals, CDialog)
ON_WM_CTLCOLOR()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
HBRUSH CFrmCashManTotals::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
int nID = pWnd->GetDlgCtrlID();
switch (nID)
{
case LBLFROM2:case LBLFROM3: case LBLACC: case LBLORDEN:
pDC->SetBkMode(OPAQUE);
pDC->SetBkColor(DarColor());
return (HBRUSH) GetStockObject(NULL_BRUSH);
}
return hbr;
}
BOOL CFrmCashManTotals::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
pDC->FillSolidRect(0,0,rect.Width(),rect.Height(),DarColor());
return TRUE;
}
where DarColor
gets the correct color based on the application look:
const COLORREF TemaCeleste = RGB(227,239,255);
const COLORREF TemaPlateado = RGB(240,241,242);
const COLORREF TemaNegro = RGB(220,222,224);
const COLORREF TemaAqua = RGB(195,202,217);
static COLORREF DarColor()
{
switch (theApp.m_nAppLook)
{
case ID_VIEW_APPLOOK_OFF_2007_BLUE: return TemaCeleste;
case ID_VIEW_APPLOOK_OFF_2007_BLACK: return TemaNegro;
case ID_VIEW_APPLOOK_OFF_2007_SILVER: return TemaPlateado;
case ID_VIEW_APPLOOK_OFF_2007_AQUA: return TemaAqua;
}
}
Printing
void CFrmAccounts::OnBnClickedBtnPrint2()
{
CMainFrame* pDato = (CMainFrame*)(AfxGetMainWnd( ));
::SetWindowLong(this->m_hWnd, GWL_ID, 0);
CCreateContext context;
context.m_pNewViewClass=RUNTIME_CLASS(CViewPrintPreview);
context.m_pCurrentDoc=NULL;
context.m_pNewDocTemplate=NULL;
context.m_pLastView=NULL;
context.m_pCurrentFrame=NULL;
CViewPrintPreview* pviewpreview =
(CViewPrintPreview*)pDato->CreateView(&context);
pviewpreview->pant = this;
pviewpreview->m_pCtrl=&m_grid;
pDato->SetActiveView(pviewpreview);
pviewpreview->OnFilePrintPreview();
ShowWindow(SW_HIDE);
}
and when you close it, switch back to the old view:
void CViewPrintPreview::OnEndPrintPreview
(CDC* pDC, CPrintInfo* pInfo, POINT point, CPreviewView* pView)
{
CView::OnEndPrintPreview(pDC, pInfo, point, pView);
CMainFrame* pDato = (CMainFrame*)(AfxGetMainWnd( ));
pDato->CambiarCantRows(nrows);
pant->ShowWindow(SW_SHOW);
pDato->SetActiveView((CView*)pant, TRUE);
pant->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
this->DestroyWindow();
}
The most important lines here are:
::SetWindowLong(this->m_hWnd, GWL_ID, 0);
pant->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
Feature Pack
I modified the ribbon (so it doesn't show the orb), the outlook control (so it doesn't show buttons) and the CaptionBar (multiline text). To do that, I created classes that inherit from the Feature Pack classes and overridden methods.
Custom Ribbon
class CCustRibbon: public CMFCRibbonBar
{
BOOL OnShowRibbonContextMenu
(CWnd* pWnd, int x, int y, CMFCRibbonBaseElement* pHit);
};
BOOL CCustRibbon::OnShowRibbonContextMenu
(CWnd* pWnd, int x, int y, CMFCRibbonBaseElement* pHit)
{
return TRUE;
}
Outlook Bar
class CControlOutLook: public CMFCOutlookBarTabCtrl
{
public:
CMFCOutlookBarToolBar* GetToolBar();
DECLARE_MESSAGE_MAP()
void RecalcLayout();
};
CMFCOutlookBarToolBar* CControlOutLook::GetToolBar()
{
return &m_wndToolBar;
}
void CControlOutLook::RecalcLayout()
{
ShowWindow(SW_HIDE);
CMFCOutlookBarTabCtrl::RecalcLayout();
GetToolBar()->EnableCustomizeButton(FALSE, 0, _T(""), FALSE);
GetToolBar()->Invalidate();
ShowWindow(SW_SHOW);
}
Caption Bar
class CBarraOutLook: public CMFCCaptionBar
{
protected:
void OnDrawText(CDC* pDC,CRect rect,const CString& strText);
DECLARE_MESSAGE_MAP()
public:
CBarraOutLook();
int AltoTexto;
void OnSize(UINT nType, int cx, int cy);
};
void CBarraOutLook::OnDrawText(CDC* pDC,CRect rect,const CString& strText)
{
CString dato = strText;
LPSTR valor = dato.GetBuffer();
CRect rectdato;
GetClientRect(&rectdato);
rect.top = rectdato.top + 9;
AltoTexto = pDC->DrawTextEx(valor, dato.GetLength(),
rect, DT_NOCLIP | DT_WORDBREAK, NULL);
AltoTexto += 14;
dato.ReleaseBuffer();
}
I'm sure there must be some easier way to "customize" the feature pack controls. But I couldn't find it.
References
The references to the articles used are in the About screen. I have modified some code and made corrections to some of them.
Other Personal Finance Software
There are many, many good double entry finance manager programs in SourceForge like GNUCash and Money Manager Ex. There is even a Personal Finance category in SourceForge.
You should check them before writing your own. If you find any that you like, you can use it and even contribute.
But, as you already know, there are a lot of "considerations" to take care with software:
- Programming languages and frameworks: They greatly impact visualization, code simplicity, speed, platforms supported, etc.
- Decisions regarding features. Many people prioritize some, other people others. In most cases, you have to choose (you cannot have both).
- Simplicity. I like software easy to use, but I also like customization and power functionality.
- Control. Eventually, someone has control of the project. If it is not unified, it forks.
Those considerations led me to write my own. I find it difficult to use personal software (for a long time, I used Excel), and I must feel comfortable with the organization software I use.
Pending
The Calendar part of the utility is not finished and it needs major improvement. In fact, that part I'm not using yet. I'm still using TodoList (another great personal organizer software). For passwords, I'm using Keepass but I'll eventually try to include it in this utility. I also have and use a rudimentary PocketPC version in my IPAQ that is not ready to post.
History
- 1st August, 2009: Initial post