1 /*******************************************************
2  Copyright (C) 2006 Madhan Kanagavel
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  ********************************************************/
18 
19 #include "mmcheckingpanel.h"
20 #include <wx/sound.h>
21 #include "paths.h"
22 #include "constants.h"
23 #include "util.h"
24 #include "mmex.h"
25 #include "mmframe.h"
26 #include "mmTips.h"
27 #include "mmSimpleDialogs.h"
28 #include "splittransactionsdialog.h"
29 #include "transdialog.h"
30 #include "validators.h"
31 #include "attachmentdialog.h"
32 #include "model/Model_Infotable.h"
33 #include "model/Model_Setting.h"
34 #include "model/Model_Checking.h"
35 #include "model/Model_Splittransaction.h"
36 #include "model/Model_Account.h"
37 #include "model/Model_Payee.h"
38 #include "model/Model_Category.h"
39 #include "model/Model_Attachment.h"
40 #include "billsdepositsdialog.h"
41 
42 #include "../resources/reconciled.xpm"
43 #include "../resources/unreconciled.xpm"
44 #include "../resources/duplicate.xpm"
45 #include "../resources/flag.xpm"
46 #include "../resources/void.xpm"
47 #include "../resources/rightarrow.xpm"
48 #include "../resources/uparrow.xpm"
49 #include "../resources/downarrow.xpm"
50 #include "../resources/tipicon.xpm"
51 #include "../resources/trash.xpm"
52 #include "../resources/attachment.xpm"
53 
54 //----------------------------------------------------------------------------
55 
56 #include <wx/srchctrl.h>
57 #include <algorithm>
58 //----------------------------------------------------------------------------
59 
60 wxBEGIN_EVENT_TABLE(mmCheckingPanel, wxPanel)
61     EVT_BUTTON(wxID_NEW,         mmCheckingPanel::OnNewTransaction)
62     EVT_BUTTON(wxID_EDIT,        mmCheckingPanel::OnEditTransaction)
63     EVT_BUTTON(wxID_REMOVE,      mmCheckingPanel::OnDeleteTransaction)
64     EVT_BUTTON(wxID_DUPLICATE,    mmCheckingPanel::OnDuplicateTransaction)
65     EVT_BUTTON(wxID_FILE, mmCheckingPanel::OnOpenAttachment)
66     EVT_MENU_RANGE(wxID_HIGHEST + MENU_VIEW_ALLTRANSACTIONS, wxID_HIGHEST + MENU_VIEW_ALLTRANSACTIONS + menu_labels().size()
67         , mmCheckingPanel::OnViewPopupSelected)
68     EVT_SEARCHCTRL_SEARCH_BTN(wxID_FIND, mmCheckingPanel::OnSearchTxtEntered)
69 wxEND_EVENT_TABLE()
70 //----------------------------------------------------------------------------
71 
72 wxBEGIN_EVENT_TABLE(TransactionListCtrl, mmListCtrl)
73     EVT_LIST_ITEM_SELECTED(wxID_ANY, TransactionListCtrl::OnListItemSelected)
74     EVT_LIST_ITEM_ACTIVATED(wxID_ANY, TransactionListCtrl::OnListItemActivated)
75     EVT_RIGHT_DOWN(TransactionListCtrl::OnMouseRightClick)
76     EVT_LEFT_DOWN(TransactionListCtrl::OnListLeftClick)
77     EVT_LIST_KEY_DOWN(wxID_ANY,  TransactionListCtrl::OnListKeyDown)
78 
79     EVT_MENU_RANGE(MENU_TREEPOPUP_MARKRECONCILED
80         , MENU_TREEPOPUP_MARKDELETE,        TransactionListCtrl::OnMarkTransaction)
81 
82     EVT_MENU_RANGE(MENU_TREEPOPUP_MARKRECONCILED_ALL
83         , MENU_TREEPOPUP_DELETE_UNRECONCILED, TransactionListCtrl::OnMarkAllTransactions)
84     EVT_MENU(MENU_TREEPOPUP_SHOWTRASH,      TransactionListCtrl::OnShowChbClick)
85 
86     EVT_MENU(MENU_TREEPOPUP_NEW2,           TransactionListCtrl::OnNewTransaction)
87     EVT_MENU(MENU_TREEPOPUP_DELETE2,        TransactionListCtrl::OnDeleteTransaction)
88     EVT_MENU(MENU_TREEPOPUP_EDIT2,          TransactionListCtrl::OnEditTransaction)
89     EVT_MENU(MENU_TREEPOPUP_MOVE2,          TransactionListCtrl::OnMoveTransaction)
90 
91     EVT_MENU(MENU_ON_COPY_TRANSACTION,      TransactionListCtrl::OnCopy)
92     EVT_MENU(MENU_ON_PASTE_TRANSACTION,     TransactionListCtrl::OnPaste)
93     EVT_MENU(MENU_ON_NEW_TRANSACTION,       TransactionListCtrl::OnNewTransaction)
94     EVT_MENU(MENU_ON_DUPLICATE_TRANSACTION, TransactionListCtrl::OnDuplicateTransaction)
95     EVT_MENU_RANGE(MENU_ON_SET_UDC0, MENU_ON_SET_UDC7, TransactionListCtrl::OnSetUserColour)
96 
97     EVT_MENU(MENU_TREEPOPUP_VIEW_SPLIT_CATEGORIES, TransactionListCtrl::OnViewSplitTransaction)
98     EVT_MENU(MENU_TREEPOPUP_ORGANIZE_ATTACHMENTS, TransactionListCtrl::OnOrganizeAttachments)
99     EVT_MENU(MENU_TREEPOPUP_CREATE_REOCCURANCE, TransactionListCtrl::OnCreateReoccurance)
100     EVT_CHAR(TransactionListCtrl::OnChar)
101 
102 wxEND_EVENT_TABLE();
103 
104 //----------------------------------------------------------------------------
105 
mmCheckingPanel(wxWindow * parent,mmGUIFrame * frame,int accountID,int id)106 mmCheckingPanel::mmCheckingPanel(wxWindow *parent, mmGUIFrame *frame, int accountID, int id)
107     : filteredBalance_(0.0)
108     , m_listCtrlAccount()
109     , m_AccountID(accountID)
110     , m_account(Model_Account::instance().get(accountID))
111     , m_currency(Model_Account::currency(m_account))
112     , transFilterDlg_(0)
113     , m_frame(frame)
114 {
115     m_basecurrecyID = Model_Infotable::instance().GetBaseCurrencyId();
116     long style = wxTAB_TRAVERSAL | wxNO_BORDER;
117     Create(parent, mmID_CHECKING, wxDefaultPosition, wxDefaultSize, style);
118 }
119 //----------------------------------------------------------------------------
120 
121 /*
122     We cannon use OnClose() event because this class deletes
123     via DestroyChildren() of its parent.
124 */
~mmCheckingPanel()125 mmCheckingPanel::~mmCheckingPanel()
126 {
127     if (transFilterDlg_) delete transFilterDlg_;
128 }
129 
Create(wxWindow * parent,wxWindowID winid,const wxPoint & pos,const wxSize & size,long style,const wxString & name)130 bool mmCheckingPanel::Create(
131     wxWindow *parent,
132     wxWindowID winid, const wxPoint& pos,
133     const wxSize& size,long style, const wxString& name
134 )
135 {
136     SetExtraStyle(GetExtraStyle()|wxWS_EX_BLOCK_EVENTS);
137     if (! wxPanel::Create(parent, winid, pos, size, style, name)) return false;
138 
139     this->windowsFreezeThaw();
140     CreateControls();
141 
142     transFilterActive_ = false;
143     transFilterDlg_    = new mmFilterTransactionsDialog(this);
144     SetTransactionFilterState(true);
145 
146     initViewTransactionsHeader();
147     initFilterSettings();
148 
149     RefreshList();
150     GetSizer()->Fit(this);
151     GetSizer()->SetSizeHints(this);
152     this->windowsFreezeThaw();
153 
154     return true;
155 }
156 
sortTable()157 void mmCheckingPanel::sortTable()
158 {
159     std::sort(this->m_trans.begin(), this->m_trans.end());
160     switch (m_listCtrlAccount->g_sortcol)
161     {
162     case TransactionListCtrl::COL_ID:
163         std::stable_sort(this->m_trans.begin(), this->m_trans.end(),SorterByTRANSID());
164         break;
165     case TransactionListCtrl::COL_NUMBER:
166         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), Model_Checking::SorterByNUMBER());
167         break;
168     case TransactionListCtrl::COL_PAYEE_STR:
169         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), SorterByPAYEENAME());
170         break;
171     case TransactionListCtrl::COL_STATUS:
172         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), SorterBySTATUS());
173         break;
174     case TransactionListCtrl::COL_CATEGORY:
175         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), SorterByCATEGNAME());
176         break;
177     case TransactionListCtrl::COL_WITHDRAWAL:
178         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), Model_Checking::SorterByWITHDRAWAL());
179         break;
180     case TransactionListCtrl::COL_DEPOSIT:
181         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), Model_Checking::SorterByDEPOSIT());
182         break;
183     case TransactionListCtrl::COL_BALANCE:
184         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), Model_Checking::SorterByBALANCE());
185         break;
186     case TransactionListCtrl::COL_NOTES:
187         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), SorterByNOTES());
188         break;
189     default:
190         std::stable_sort(this->m_trans.begin(), this->m_trans.end(), SorterByTRANSDATE());
191         break;
192     }
193 
194     if (!m_listCtrlAccount->g_asc) std::reverse(this->m_trans.begin(), this->m_trans.end());
195 }
196 
filterTable()197 void mmCheckingPanel::filterTable()
198 {
199     this->m_trans.clear();
200     account_balance_ = m_account ? m_account->INITIALBAL : 0.0;
201     reconciled_balance_ = account_balance_;
202     filteredBalance_ = 0.0;
203 
204     const auto splits = Model_Splittransaction::instance().get_all();
205     const auto attachments = Model_Attachment::instance().get_all(Model_Attachment::TRANSACTION);
206     for (const auto& tran : Model_Account::transaction(this->m_account))
207     {
208         double transaction_amount = Model_Checking::amount(tran, m_AccountID);
209         if (Model_Checking::status(tran.STATUS) != Model_Checking::VOID_)
210             account_balance_ += transaction_amount;
211         else
212         {
213             if (!m_listCtrlAccount->showDeletedTransactions_)
214                 continue;
215         }
216         if (Model_Checking::status(tran.STATUS) == Model_Checking::RECONCILED)
217             reconciled_balance_ += transaction_amount;
218 
219         if (transFilterActive_)
220         {
221             if (!transFilterDlg_->checkAll(tran, m_AccountID, splits))
222                 continue;
223         }
224         else
225         {
226             if (!Model_Checking::TRANSDATE(tran).IsBetween(quickFilterBeginDate_, quickFilterEndDate_))
227                 continue;
228         }
229 
230         Model_Checking::Full_Data full_tran(tran, splits);
231         full_tran.PAYEENAME = full_tran.real_payee_name(m_AccountID);
232         full_tran.BALANCE = account_balance_;
233         full_tran.AMOUNT = transaction_amount;
234         filteredBalance_ += transaction_amount;
235 
236         if (attachments.count(full_tran.TRANSID))
237             full_tran.NOTES.Prepend(mmAttachmentManage::GetAttachmentNoteSign());
238 
239         this->m_trans.push_back(full_tran);
240     }
241 }
242 
updateTable()243 void mmCheckingPanel::updateTable()
244 {
245     account_balance_ = 0.0;
246     reconciled_balance_ = 0.0;
247     if (m_account)
248     {
249         account_balance_ = m_account->INITIALBAL;
250         reconciled_balance_ = account_balance_;
251     }
252     for (const auto& tran : Model_Account::transaction(m_account))
253     {
254         double transaction_amount = Model_Checking::amount(tran, m_AccountID);
255         if (Model_Checking::status(tran.STATUS) != Model_Checking::VOID_)
256             account_balance_ += transaction_amount;
257         reconciled_balance_ += Model_Checking::reconciled(tran, m_AccountID);
258     }
259     filteredBalance_ = 0.0;
260     for (const auto & tran : m_trans)
261     {
262         filteredBalance_ += Model_Checking::amount(tran, m_AccountID);
263     }
264 
265     setAccountSummary();
266 
267     if (m_listCtrlAccount->g_sortcol == TransactionListCtrl::COL_STATUS)
268     {
269         sortTable();
270         m_listCtrlAccount->RefreshItems(0, m_trans.size() - 1);
271     }
272 }
273 
markSelectedTransaction(int trans_id)274 void mmCheckingPanel::markSelectedTransaction(int trans_id)
275 {
276     m_listCtrlAccount->m_selectedIndex = -1;
277     m_listCtrlAccount->m_selectedID = -1;
278     if (trans_id > 0)
279     {
280         long i = 0;
281         for (const auto & tran : m_trans)
282         {
283             if (trans_id == tran.TRANSID)
284             {
285                 m_listCtrlAccount->m_selectedIndex = i;
286                 m_listCtrlAccount->m_selectedID = trans_id;
287                 break;
288             }
289             ++i;
290         }
291     }
292 
293     if (!m_trans.empty() && m_listCtrlAccount->m_selectedIndex < 0)
294     {
295         if (m_listCtrlAccount->g_asc)
296             m_listCtrlAccount->EnsureVisible(static_cast<long>(m_trans.size()) - 1);
297         else
298             m_listCtrlAccount->EnsureVisible(0);
299     }
300     else
301     {
302         enableEditDeleteButtons(false);
303         showTips();
304     }
305 }
306 //----------------------------------------------------------------------------
307 
OnMouseLeftDown(wxMouseEvent & event)308 void mmCheckingPanel::OnMouseLeftDown( wxMouseEvent& event )
309 {
310     // depending on the clicked control's window id.
311     switch( event.GetId() )
312     {
313         case ID_PANEL_CHECKING_STATIC_BITMAP_FILTER :
314         {
315             wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, ID_PANEL_CHECKING_STATIC_BITMAP_FILTER);
316             GetEventHandler()->AddPendingEvent(ev);
317 
318             break;
319         }
320         default:
321         {
322             wxMenu menu;
323             int id = wxID_HIGHEST + MENU_VIEW_ALLTRANSACTIONS;
324             for (const auto& i : menu_labels())
325             {
326                 menu.Append(id++, wxGetTranslation(i));
327             }
328             PopupMenu(&menu, event.GetPosition());
329 
330             break;
331         }
332     }
333     event.Skip();
334 }
335 
336 //----------------------------------------------------------------------------
337 
CreateControls()338 void mmCheckingPanel::CreateControls()
339 {
340     int border = 1;
341     wxSizerFlags flags = wxSizerFlags(g_flags).Border(wxALL, border);
342     wxSizerFlags flagsExpand = wxSizerFlags(g_flagsExpand).Border(wxALL, border);
343 
344     wxBoxSizer* itemBoxSizer9 = new wxBoxSizer(wxVERTICAL);
345     this->SetSizer(itemBoxSizer9);
346 
347     /* ---------------------- */
348     wxPanel* headerPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition
349         , wxDefaultSize, wxNO_BORDER | wxTAB_TRAVERSAL);
350     itemBoxSizer9->Add(headerPanel, flags);
351 
352     wxBoxSizer* itemBoxSizerVHeader = new wxBoxSizer(wxVERTICAL);
353     headerPanel->SetSizer(itemBoxSizerVHeader);
354 
355     wxGridSizer* itemBoxSizerVHeader2 = new wxGridSizer(0, 1, 5, 20);
356     itemBoxSizerVHeader->Add(itemBoxSizerVHeader2);
357 
358     header_text_ = new wxStaticText( headerPanel, wxID_STATIC, "");
359     header_text_->SetFont(this->GetFont().Larger().Bold());
360     itemBoxSizerVHeader2->Add(header_text_, flags);
361 
362     wxBoxSizer* itemBoxSizerHHeader2 = new wxBoxSizer(wxHORIZONTAL);
363     wxFlexGridSizer* itemFlexGridSizerHHeader2 = new wxFlexGridSizer(5, 1, 1);
364     itemBoxSizerVHeader2->Add(itemBoxSizerHHeader2);
365     itemBoxSizerHHeader2->Add(itemFlexGridSizerHHeader2);
366 
367     wxBitmap itemStaticBitmap(rightarrow_xpm);
368     bitmapMainFilter_ = new wxStaticBitmap(headerPanel, wxID_PAGE_SETUP
369         , itemStaticBitmap);
370     itemFlexGridSizerHHeader2->Add(bitmapMainFilter_, flags);
371     bitmapMainFilter_->Connect(wxID_ANY, wxEVT_RIGHT_DOWN
372         , wxMouseEventHandler(mmCheckingPanel::OnFilterResetToViewAll), nullptr, this);
373     bitmapMainFilter_->Connect(wxID_ANY, wxEVT_LEFT_DOWN
374         , wxMouseEventHandler(mmCheckingPanel::OnMouseLeftDown), nullptr, this);
375 
376     stxtMainFilter_ = new wxStaticText(headerPanel, wxID_ANY, "", wxDefaultPosition, wxSize(250, -1));
377     itemFlexGridSizerHHeader2->Add(stxtMainFilter_, flags);
378 
379     itemFlexGridSizerHHeader2->AddSpacer(20);
380 
381     bitmapTransFilter_ = new wxStaticBitmap(headerPanel, ID_PANEL_CHECKING_STATIC_BITMAP_FILTER
382         , itemStaticBitmap);
383     itemFlexGridSizerHHeader2->Add(bitmapTransFilter_, flags);
384     bitmapTransFilter_->Connect(wxID_ANY, wxEVT_LEFT_DOWN
385         , wxMouseEventHandler(mmCheckingPanel::OnFilterTransactions), nullptr, this);
386     bitmapTransFilter_->Connect(wxID_ANY, wxEVT_RIGHT_DOWN
387         , wxMouseEventHandler(mmCheckingPanel::OnFilterTransactions), nullptr, this);
388 
389     statTextTransFilter_ = new wxStaticText(headerPanel, wxID_ANY
390         , _("Transaction Filter"));
391     itemFlexGridSizerHHeader2->Add(statTextTransFilter_, flags);
392 
393     wxStaticText* itemStaticText12 = new wxStaticText(headerPanel
394         , ID_PANEL_CHECKING_STATIC_BALHEADER1, "$", wxDefaultPosition, wxSize(120, -1));
395     wxStaticText* itemStaticText14 = new wxStaticText(headerPanel
396         , ID_PANEL_CHECKING_STATIC_BALHEADER2, "$", wxDefaultPosition, wxSize(120, -1));
397     wxStaticText* itemStaticText16 = new wxStaticText(headerPanel
398         , ID_PANEL_CHECKING_STATIC_BALHEADER3, "$", wxDefaultPosition, wxSize(120, -1));
399     wxStaticText* itemStaticText17 = new wxStaticText(headerPanel
400         , ID_PANEL_CHECKING_STATIC_BALHEADER4, _("Displayed Bal: "));
401     wxStaticText* itemStaticText18 = new wxStaticText(headerPanel
402         , ID_PANEL_CHECKING_STATIC_BALHEADER5, "$", wxDefaultPosition, wxSize(120, -1));
403 
404     wxFlexGridSizer* balances_header = new wxFlexGridSizer(0,8,5,10);
405     itemBoxSizerVHeader->Add(balances_header, flagsExpand);
406     balances_header->Add(new wxStaticText(headerPanel, wxID_STATIC, _("Account Bal: ")));
407     balances_header->Add(itemStaticText12);
408     balances_header->Add(new wxStaticText(headerPanel,  wxID_STATIC, _("Reconciled Bal: ")));
409     balances_header->Add(itemStaticText14);
410     balances_header->Add(new wxStaticText(headerPanel, wxID_STATIC, _("Diff: ")));
411     balances_header->Add(itemStaticText16);
412     balances_header->Add(itemStaticText17);
413     balances_header->Add(itemStaticText18);
414 
415     /* ---------------------- */
416 
417     wxSplitterWindow* itemSplitterWindow10 = new wxSplitterWindow(this
418         , wxID_ANY, wxDefaultPosition, wxSize(200, 200)
419         , wxSP_3DBORDER | wxSP_3DSASH | wxNO_BORDER);
420 
421     wxSize imageSize(16, 16);
422     m_imageList.reset(new wxImageList(imageSize.GetWidth(), imageSize.GetHeight()));
423     m_imageList->Add(wxImage(reconciled_xpm).Scale(16, 16));
424     m_imageList->Add(wxImage(void_xpm).Scale(16, 16));
425     m_imageList->Add(wxImage(flag_xpm).Scale(16, 16));
426     m_imageList->Add(wxImage(unreconciled_xpm).Scale(16, 16));
427     m_imageList->Add(wxImage(uparrow_xpm).Scale(16, 16));
428     m_imageList->Add(wxImage(downarrow_xpm).Scale(16, 16));
429     m_imageList->Add(wxImage(duplicate_xpm).Scale(16, 16));
430     m_imageList->Add(wxImage(trash_xpm).Scale(16, 16));
431 
432     m_listCtrlAccount = new TransactionListCtrl(this, itemSplitterWindow10
433         , wxID_ANY);
434 
435     m_listCtrlAccount->SetImageList(m_imageList.get(), wxIMAGE_LIST_SMALL);
436     m_listCtrlAccount->setSortOrder(m_listCtrlAccount->g_asc);
437     m_listCtrlAccount->setSortColumn(m_listCtrlAccount->g_sortcol);
438     m_listCtrlAccount->SetFocus();
439 
440     m_listCtrlAccount->createColumns(*m_listCtrlAccount);
441 
442     // load the global variables
443     long val = m_listCtrlAccount->COL_DEF_SORT;
444     wxString strVal = Model_Setting::instance().GetStringSetting("CHECK_SORT_COL", wxString() << val);
445     if (strVal.ToLong(&val)) m_listCtrlAccount->g_sortcol = m_listCtrlAccount->toEColumn(val);
446     // --
447     val = 1; // asc sorting default
448     strVal = Model_Setting::instance().GetStringSetting("CHECK_ASC", wxString() << val);
449     if (strVal.ToLong(&val)) m_listCtrlAccount->g_asc = val != 0;
450 
451     // --
452     m_listCtrlAccount->setSortColumn(m_listCtrlAccount->g_sortcol);
453     m_listCtrlAccount->setSortOrder(m_listCtrlAccount->g_asc);
454     m_listCtrlAccount->setColumnImage(m_listCtrlAccount->getSortColumn()
455         , m_listCtrlAccount->getSortOrder() ? m_listCtrlAccount->ICON_ASC : m_listCtrlAccount->ICON_DESC); // asc\desc sort mark (arrow)
456 
457     wxPanel *itemPanel12 = new wxPanel(itemSplitterWindow10, wxID_ANY
458         , wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxTAB_TRAVERSAL);
459 
460     itemSplitterWindow10->SplitHorizontally(m_listCtrlAccount, itemPanel12);
461     itemSplitterWindow10->SetMinimumPaneSize(100);
462     itemSplitterWindow10->SetSashGravity(1.0);
463 
464     itemBoxSizer9->Add(itemSplitterWindow10, flagsExpand);
465 
466     wxBoxSizer* itemBoxSizer4 = new wxBoxSizer(wxVERTICAL);
467     itemPanel12->SetSizer(itemBoxSizer4);
468 
469     wxBoxSizer* itemButtonsSizer = new wxBoxSizer(wxHORIZONTAL);
470     itemBoxSizer4->Add(itemButtonsSizer, flags);
471 
472     btnNew_ = new wxButton(itemPanel12, wxID_NEW, _("&New "));
473     btnNew_->SetToolTip(_("New Transaction"));
474     itemButtonsSizer->Add(btnNew_, 0, wxRIGHT, 5);
475 
476     btnEdit_ = new wxButton(itemPanel12, wxID_EDIT, _("&Edit "));
477     btnEdit_->SetToolTip(_("Edit selected transaction"));
478     itemButtonsSizer->Add(btnEdit_, 0, wxRIGHT, 5);
479     btnEdit_->Enable(false);
480 
481     btnDelete_ = new wxButton(itemPanel12, wxID_REMOVE, _("&Delete "));
482     btnDelete_->SetToolTip(_("Delete selected transaction"));
483     itemButtonsSizer->Add(btnDelete_, 0, wxRIGHT, 5);
484     btnDelete_->Enable(false);
485 
486     btnDuplicate_ = new wxButton(itemPanel12, wxID_DUPLICATE, _("D&uplicate "));
487     btnDuplicate_->SetToolTip(_("Duplicate selected transaction"));
488     itemButtonsSizer->Add(btnDuplicate_, 0, wxRIGHT, 5);
489     btnDuplicate_->Enable(false);
490 
491     btnAttachment_ = new wxBitmapButton(itemPanel12, wxID_FILE
492         , wxBitmap(attachment_xpm), wxDefaultPosition
493         , wxSize(30, btnDuplicate_->GetSize().GetY()));
494     btnAttachment_->SetToolTip(_("Open attachments"));
495     itemButtonsSizer->Add(btnAttachment_, 0, wxRIGHT, 5);
496 	btnAttachment_->Enable(false);
497 
498     wxSearchCtrl* searchCtrl = new wxSearchCtrl(itemPanel12
499         , wxID_FIND, wxEmptyString, wxDefaultPosition
500         , wxSize(100, btnDuplicate_->GetSize().GetHeight())
501         , wxTE_NOHIDESEL, wxDefaultValidator);
502     searchCtrl->SetDescriptiveText(_("Search"));
503     itemButtonsSizer->Add(searchCtrl, 0, wxCENTER, 1);
504     searchCtrl->SetToolTip(_("Enter any string to find it in the nearest transaction notes"));
505 
506     //Infobar-mini
507     info_panel_mini_ = new wxStaticText(itemPanel12, wxID_STATIC, "");
508     itemButtonsSizer->Add(info_panel_mini_, 1, wxGROW | wxTOP | wxLEFT, 5);
509 
510     //Infobar
511     info_panel_ = new wxStaticText(itemPanel12
512         , wxID_STATIC, "", wxDefaultPosition, wxSize(200, -1), wxTE_MULTILINE | wxTE_WORDWRAP);
513     itemBoxSizer4->Add(info_panel_, flagsExpand);
514     //Show tips when no any transaction selected
515     showTips();
516 }
517 
518 //----------------------------------------------------------------------------
GetPanelTitle(const Model_Account::Data & account) const519 wxString mmCheckingPanel::GetPanelTitle(const Model_Account::Data& account) const
520 {
521     return wxString::Format(_("Account View : %s"), account.ACCOUNTNAME);
522 }
523 
BuildPage() const524 wxString mmCheckingPanel::BuildPage() const
525 {
526     Model_Account::Data *account = Model_Account::instance().get(m_AccountID);
527     return m_listCtrlAccount->BuildPage((account ? GetPanelTitle(*account) : ""));
528 }
529 
setAccountSummary()530 void mmCheckingPanel::setAccountSummary()
531 {
532     Model_Account::Data *account = Model_Account::instance().get(m_AccountID);
533 
534     if (account)
535         header_text_->SetLabelText(GetPanelTitle(*account));
536 
537     bool show_displayed_balance_ = (transFilterActive_ || currentView_ != MENU_VIEW_ALLTRANSACTIONS);
538     wxStaticText* header = (wxStaticText*)FindWindow(ID_PANEL_CHECKING_STATIC_BALHEADER1);
539     header->SetLabelText(Model_Account::toCurrency(account_balance_, account));
540     header = (wxStaticText*)FindWindow(ID_PANEL_CHECKING_STATIC_BALHEADER2);
541     header->SetLabelText(Model_Account::toCurrency(reconciled_balance_, account));
542     header = (wxStaticText*)FindWindow(ID_PANEL_CHECKING_STATIC_BALHEADER3);
543     header->SetLabelText(Model_Account::toCurrency(account_balance_ - reconciled_balance_, account));
544     header = (wxStaticText*)FindWindow(ID_PANEL_CHECKING_STATIC_BALHEADER4);
545     header->SetLabelText(show_displayed_balance_
546         ? _("Displayed Bal: ") : "");
547     header = (wxStaticText*)FindWindow(ID_PANEL_CHECKING_STATIC_BALHEADER5);
548     header->SetLabelText(show_displayed_balance_
549         ? Model_Account::toCurrency(filteredBalance_, account) : "");
550     this->Layout();
551 }
552 
553 //----------------------------------------------------------------------------
enableEditDeleteButtons(bool en)554 void mmCheckingPanel::enableEditDeleteButtons(bool en)
555 {
556     if (m_listCtrlAccount->GetSelectedItemCount()>1)
557     {
558         btnEdit_->Enable(false);
559         btnDelete_->Enable(true);
560         btnDuplicate_->Enable(false);
561         btnAttachment_->Enable(false);
562     }
563     else
564     {
565         btnEdit_->Enable(en);
566         btnDelete_->Enable(en);
567         btnDuplicate_->Enable(en);
568         btnAttachment_->Enable(en);
569     }
570 }
571 //----------------------------------------------------------------------------
572 
updateExtraTransactionData(int selIndex)573 void mmCheckingPanel::updateExtraTransactionData(int selIndex)
574 {
575     if (selIndex > -1)
576     {
577         enableEditDeleteButtons(true);
578         const Model_Checking::Data& tran = this->m_trans.at(selIndex);
579         Model_Checking::Full_Data full_tran(tran);
580         info_panel_->SetLabelText(tran.NOTES);
581         wxString miniStr = full_tran.info();
582 
583         //Show only first line but full string set as tooltip
584         if (miniStr.Find("\n") > 1 && !miniStr.IsEmpty())
585         {
586             info_panel_mini_->SetLabelText(miniStr.substr(0, miniStr.Find("\n")) + " ...");
587             info_panel_mini_->SetToolTip(miniStr);
588         }
589         else
590         {
591             info_panel_mini_->SetLabelText(miniStr);
592             info_panel_mini_->SetToolTip(miniStr);
593         }
594 
595     }
596     else
597     {
598         info_panel_mini_->SetLabelText("");
599         enableEditDeleteButtons(false);
600         showTips();
601     }
602 }
603 //----------------------------------------------------------------------------
showTips()604 void mmCheckingPanel::showTips()
605 {
606     info_panel_->SetLabelText(wxGetTranslation(TIPS[rand() % (sizeof(TIPS) / sizeof(wxString))]));
607 }
608 //----------------------------------------------------------------------------
609 
OnDeleteTransaction(wxCommandEvent & event)610 void mmCheckingPanel::OnDeleteTransaction(wxCommandEvent& event)
611 {
612     m_listCtrlAccount->OnDeleteTransaction(event);
613 }
614 //----------------------------------------------------------------------------
615 
OnNewTransaction(wxCommandEvent & event)616 void mmCheckingPanel::OnNewTransaction(wxCommandEvent& event)
617 {
618    m_listCtrlAccount->OnNewTransaction(event);
619 }
620 //----------------------------------------------------------------------------
621 
OnEditTransaction(wxCommandEvent & event)622 void mmCheckingPanel::OnEditTransaction(wxCommandEvent& event)
623 {
624     m_listCtrlAccount->OnEditTransaction(event);
625     m_listCtrlAccount->SetFocus();
626 }
627 //----------------------------------------------------------------------------
628 
OnDuplicateTransaction(wxCommandEvent & event)629 void mmCheckingPanel::OnDuplicateTransaction(wxCommandEvent& event)
630 {
631     m_listCtrlAccount->OnDuplicateTransaction(event);
632 }
633 //----------------------------------------------------------------------------
634 
OnMoveTransaction(wxCommandEvent & event)635 void mmCheckingPanel::OnMoveTransaction(wxCommandEvent& event)
636 {
637     m_listCtrlAccount->OnMoveTransaction(event);
638 }
639 //----------------------------------------------------------------------------
640 
OnOpenAttachment(wxCommandEvent & event)641 void mmCheckingPanel::OnOpenAttachment(wxCommandEvent& event)
642 {
643     m_listCtrlAccount->OnOpenAttachment(event);
644     m_listCtrlAccount->SetFocus();
645 }
646 //----------------------------------------------------------------------------
647 
648 
initViewTransactionsHeader()649 void mmCheckingPanel::initViewTransactionsHeader()
650 {
651     const wxString& def_view = Model_Setting::instance().ViewTransactions();
652     currentView_ = menu_labels().Index(Model_Infotable::instance().GetStringInfo(wxString::Format("CHECK_FILTER_ID_%d", m_AccountID), def_view));
653     if (currentView_ < 0 || currentView_ >= (int) menu_labels().size())
654         currentView_ = menu_labels().Index(VIEW_TRANS_ALL_STR);
655 
656     SetTransactionFilterState(currentView_ == MENU_VIEW_ALLTRANSACTIONS);
657     stxtMainFilter_->SetLabelText(wxGetTranslation(menu_labels()[currentView_]));
658 }
659 //----------------------------------------------------------------------------
initFilterSettings()660 void mmCheckingPanel::initFilterSettings()
661 {
662     mmDateRange* date_range = 0;
663 
664     if (transFilterActive_)
665         date_range = new mmAllTime;
666     else if (currentView_ == MENU_VIEW_ALLTRANSACTIONS)
667         date_range = new mmAllTime;
668     else if (currentView_ == MENU_VIEW_TODAY)
669         date_range = new mmToday;
670     else if (currentView_ == MENU_VIEW_CURRENTMONTH)
671         date_range = new mmCurrentMonth;
672     else if (currentView_ == MENU_VIEW_LAST30)
673         date_range = new mmLast30Days;
674     else if (currentView_ == MENU_VIEW_LAST90)
675         date_range = new mmLast90Days;
676     else if (currentView_ == MENU_VIEW_LASTMONTH)
677         date_range = new mmLastMonth;
678     else if (currentView_ == MENU_VIEW_LAST3MONTHS)
679         date_range = new mmLast3Months;
680     else if (currentView_ == MENU_VIEW_LAST12MONTHS)
681         date_range = new mmLast12Months;
682     else if (currentView_ == MENU_VIEW_CURRENTYEAR)
683         date_range = new mmCurrentYear;
684     else
685         wxASSERT(false);
686 
687     if (date_range)
688     {
689         quickFilterBeginDate_ = date_range->start_date();
690         quickFilterEndDate_ = date_range->end_date();
691         delete date_range;
692     }
693 }
694 
OnFilterResetToViewAll(wxMouseEvent & event)695 void mmCheckingPanel::OnFilterResetToViewAll(wxMouseEvent& event) {
696 
697     if (currentView_ == MENU_VIEW_ALLTRANSACTIONS)
698     {
699         event.Skip();
700         return;
701     }
702 
703     currentView_ = MENU_VIEW_ALLTRANSACTIONS;
704     stxtMainFilter_->SetLabelText(wxGetTranslation(menu_labels()[currentView_]));
705     SetTransactionFilterState(true);
706     initFilterSettings();
707 
708     m_listCtrlAccount->m_selectedIndex = -1;
709     RefreshList();
710     this->Layout();
711 }
712 
OnViewPopupSelected(wxCommandEvent & event)713 void mmCheckingPanel::OnViewPopupSelected(wxCommandEvent& event)
714 {
715     currentView_ = event.GetId() - wxID_HIGHEST;
716 
717     if (currentView_ == MENU_VIEW_ALLTRANSACTIONS)
718         transFilterActive_ = false;
719 
720     stxtMainFilter_->SetLabelText(wxGetTranslation(menu_labels()[currentView_]));
721     SetTransactionFilterState(currentView_ == MENU_VIEW_ALLTRANSACTIONS);
722 
723     m_listCtrlAccount->m_selectedIndex = -1;
724 
725     Model_Infotable::instance().Set(wxString::Format("CHECK_FILTER_ID_%ld", (long) m_AccountID)
726         , menu_labels()[currentView_]);
727     initFilterSettings();
728     RefreshList(m_listCtrlAccount->m_selectedID);
729     stxtMainFilter_->Layout();
730 }
731 
DeleteViewedTransactions()732 void mmCheckingPanel::DeleteViewedTransactions()
733 {
734     Model_Checking::instance().Savepoint();
735     for (const auto& tran: this->m_trans)
736     {
737         // remove also removes any split transactions
738         Model_Checking::instance().remove(tran.TRANSID);
739         mmAttachmentManage::DeleteAllAttachments(Model_Attachment::reftype_desc(Model_Attachment::TRANSACTION), tran.TRANSID);
740         if (m_listCtrlAccount->m_selectedForCopy == tran.TRANSID) m_listCtrlAccount->m_selectedForCopy = -1;
741     }
742     Model_Checking::instance().ReleaseSavepoint();
743 }
744 
DeleteFlaggedTransactions(const wxString & status)745 void mmCheckingPanel::DeleteFlaggedTransactions(const wxString& status)
746 {
747     Model_Checking::instance().Savepoint();
748     for (const auto& tran: this->m_trans)
749     {
750         if (tran.STATUS == status)
751         {
752             // remove also removes any split transactions
753             Model_Checking::instance().remove(tran.TRANSID);
754             mmAttachmentManage::DeleteAllAttachments(Model_Attachment::reftype_desc(Model_Attachment::TRANSACTION), tran.TRANSID);
755             if (m_listCtrlAccount->m_selectedForCopy == tran.TRANSID) m_listCtrlAccount->m_selectedForCopy = -1;
756         }
757     }
758     Model_Checking::instance().ReleaseSavepoint();
759 }
760 
OnFilterTransactions(wxMouseEvent & event)761 void mmCheckingPanel::OnFilterTransactions(wxMouseEvent& event)
762 {
763     int e = event.GetEventType();
764 
765     wxBitmap bitmapFilterIcon(rightarrow_xpm);
766 
767     if (e == wxEVT_LEFT_DOWN) {
768         transFilterDlg_->setAccountToolTip(_("Select account used in transfer transactions"));
769         if (transFilterDlg_->ShowModal() == wxID_OK && transFilterDlg_->somethingSelected())
770         {
771             transFilterActive_ = true;
772             wxBitmap activeBitmapFilterIcon(tipicon_xpm);
773             bitmapFilterIcon = activeBitmapFilterIcon;
774         }
775         else
776         {
777             transFilterActive_ = false;
778         }
779 
780     } else {
781         if (transFilterActive_ == false) return;
782         transFilterActive_ = false;
783     }
784 
785     wxImage pic = bitmapFilterIcon.ConvertToImage();
786     bitmapTransFilter_->SetBitmap(pic);
787     SetTransactionFilterState(true);
788 
789     RefreshList();
790 }
791 
792 
getItem(long item,long column)793 const wxString mmCheckingPanel::getItem(long item, long column)
794 {
795     if (item < 0 || item >= (int)m_trans.size()) return "";
796 
797     const Model_Checking::Full_Data& tran = this->m_trans.at(item);
798     switch (column)
799     {
800     case TransactionListCtrl::COL_ID:
801         return wxString::Format("%i", tran.TRANSID).Trim();
802     case TransactionListCtrl::COL_DATE:
803         return mmGetDateForDisplay(Model_Checking::TRANSDATE(tran));
804     case TransactionListCtrl::COL_NUMBER:
805         return tran.TRANSACTIONNUMBER;
806     case TransactionListCtrl::COL_CATEGORY:
807         return tran.CATEGNAME;
808     case TransactionListCtrl::COL_PAYEE_STR:
809         return tran.PAYEENAME;
810     case TransactionListCtrl::COL_STATUS:
811         return tran.STATUS;
812     case TransactionListCtrl::COL_WITHDRAWAL:
813         return tran.AMOUNT <= 0 ? Model_Currency::toString(fabs(tran.AMOUNT), this->m_currency) : "";
814     case TransactionListCtrl::COL_DEPOSIT:
815         return tran.AMOUNT > 0 ? Model_Currency::toString(tran.AMOUNT, this->m_currency) : "";
816     case TransactionListCtrl::COL_BALANCE:
817         return Model_Currency::toString(tran.BALANCE, this->m_currency);
818     case TransactionListCtrl::COL_NOTES:
819         return tran.NOTES;
820     default:
821         return wxEmptyString;
822     }
823 }
824 
OnSearchTxtEntered(wxCommandEvent & event)825 void mmCheckingPanel::OnSearchTxtEntered(wxCommandEvent& event)
826 {
827     const wxString search_string = event.GetString().Lower();
828     if (search_string.IsEmpty()) return;
829 
830     long last = m_listCtrlAccount->GetItemCount() - 1;
831     long selectedItem = m_listCtrlAccount->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
832     if (selectedItem <= 0 || selectedItem >= last) //nothing selected
833         selectedItem = m_listCtrlAccount->g_asc ? last : 0;
834 
835     while (selectedItem >= 0 && selectedItem <= last)
836     {
837         m_listCtrlAccount->g_asc ?  selectedItem-- : selectedItem++;
838         const wxString t = getItem(selectedItem, m_listCtrlAccount->COL_NOTES).Lower();
839         if (t.Matches(search_string + "*"))
840         {
841             //First of all any items should be unselected
842             long cursel = m_listCtrlAccount->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
843             if (cursel != wxNOT_FOUND)
844                 m_listCtrlAccount->SetItemState(cursel, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
845 
846             //Then finded item will be selected
847             m_listCtrlAccount->SetItemState(selectedItem, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
848             m_listCtrlAccount->EnsureVisible(selectedItem);
849             break;
850         }
851     }
852 }
853 
DisplaySplitCategories(int transID)854 void mmCheckingPanel::DisplaySplitCategories(int transID)
855 {
856     const Model_Checking::Data* tran = Model_Checking::instance().get(transID);
857     int transType = Model_Checking::type(tran->TRANSCODE);
858 
859     Model_Checking::Data *transaction = Model_Checking::instance().get(transID);
860     auto splits = Model_Checking::splittransaction(transaction);
861     std::vector<Split> splt;
862     for (const auto& entry : splits) {
863         Split s;
864         s.CATEGID = entry.CATEGID;
865         s.SUBCATEGID = entry.SUBCATEGID;
866         s.SPLITTRANSAMOUNT = entry.SPLITTRANSAMOUNT;
867         splt.push_back(s);
868     }
869     SplitTransactionDialog splitTransDialog(this
870         , splt
871         , transType
872         , m_AccountID);
873 
874     splitTransDialog.SetDisplaySplitCategories();
875     splitTransDialog.ShowModal();
876 }
877 
RefreshList(int transID)878 void mmCheckingPanel::RefreshList(int transID)
879 {
880     m_listCtrlAccount->refreshVisualList(transID);
881 }
882 
SetTransactionFilterState(bool active)883 void mmCheckingPanel::SetTransactionFilterState(bool active)
884 {
885     bitmapMainFilter_->Enable(!transFilterActive_);
886     stxtMainFilter_->Enable(!transFilterActive_);
887     //bitmapTransFilter_->Enable(active || transFilterActive_);
888     //statTextTransFilter_->Enable(active || transFilterActive_);
889 }
890 
SetSelectedTransaction(int transID)891 void mmCheckingPanel::SetSelectedTransaction(int transID)
892 {
893     RefreshList(transID);
894     m_listCtrlAccount->SetFocus();
895 }
896 
897 // Refresh account screen with new details
DisplayAccountDetails(int accountID)898 void mmCheckingPanel::DisplayAccountDetails(int accountID)
899 {
900     m_AccountID = accountID;
901     m_account = Model_Account::instance().get(accountID);
902     if (m_account)
903         m_currency = Model_Account::currency(m_account);
904 
905     initViewTransactionsHeader();
906     initFilterSettings();
907     if (m_listCtrlAccount->m_selectedIndex > -1)
908         m_listCtrlAccount->SetItemState(m_listCtrlAccount->m_selectedIndex, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
909     m_listCtrlAccount->m_selectedIndex = -1;
910     RefreshList();
911     showTips();
912 }
913 
mmPlayTransactionSound()914 void mmCheckingPanel::mmPlayTransactionSound()
915 {
916     bool play = Model_Setting::instance().GetBoolSetting(INIDB_USE_TRANSACTION_SOUND, true);
917     if (play)
918     {
919         wxString wav_path = mmex::getPathResource(mmex::TRANS_SOUND);
920         wxLogDebug("%s", wav_path);
921         wxSound registerSound(wav_path);
922 
923         if (registerSound.IsOk())
924             registerSound.Play(wxSOUND_ASYNC);
925     }
926 }
927 //----------------------------------------------------------------------------
928 
TransactionListCtrl(mmCheckingPanel * cp,wxWindow * parent,const wxWindowID id)929 TransactionListCtrl::TransactionListCtrl(
930     mmCheckingPanel *cp,
931     wxWindow *parent,
932     const wxWindowID id
933 ) :
934     mmListCtrl(parent, id),
935     m_cp(cp),
936     m_selectedIndex(-1),
937     m_selectedForCopy(-1),
938     m_attr1(*wxBLACK, mmColors::listAlternativeColor0, wxNullFont),
939     m_attr2(*wxBLACK, mmColors::listAlternativeColor1, wxNullFont),
940     m_attr3(mmColors::listFutureDateColor, mmColors::listAlternativeColor0, wxNullFont),
941     m_attr4(mmColors::listFutureDateColor, mmColors::listAlternativeColor1, wxNullFont),
942     m_attr11(*wxBLACK, mmColors::userDefColor1, wxNullFont),
943     m_attr12(*wxBLACK, mmColors::userDefColor2, wxNullFont),
944     m_attr13(*wxBLACK, mmColors::userDefColor3, wxNullFont),
945     m_attr14(*wxBLACK, mmColors::userDefColor4, wxNullFont),
946     m_attr15(*wxBLACK, mmColors::userDefColor5, wxNullFont),
947     m_attr16(*wxYELLOW, mmColors::userDefColor6, wxNullFont),
948     m_attr17(*wxYELLOW, mmColors::userDefColor7, wxNullFont),
949     m_sortCol(COL_DEF_SORT),
950     g_sortcol(COL_DEF_SORT),
951     g_asc(true),
952     m_selectedID(-1),
953     topItemIndex_(-1)
954 {
955     wxASSERT(m_cp);
956 
957     const wxAcceleratorEntry entries[] =
958     {
959         wxAcceleratorEntry(wxACCEL_CTRL, 'C', MENU_ON_COPY_TRANSACTION),
960         wxAcceleratorEntry(wxACCEL_CTRL, 'V', MENU_ON_PASTE_TRANSACTION),
961         wxAcceleratorEntry(wxACCEL_ALT,  'N', MENU_ON_NEW_TRANSACTION),
962         wxAcceleratorEntry(wxACCEL_CTRL, 'D', MENU_ON_DUPLICATE_TRANSACTION),
963         wxAcceleratorEntry(wxACCEL_CTRL, '0', MENU_ON_SET_UDC0),
964         wxAcceleratorEntry(wxACCEL_CTRL, '1', MENU_ON_SET_UDC1),
965         wxAcceleratorEntry(wxACCEL_CTRL, '2', MENU_ON_SET_UDC2),
966         wxAcceleratorEntry(wxACCEL_CTRL, '3', MENU_ON_SET_UDC3),
967         wxAcceleratorEntry(wxACCEL_CTRL, '4', MENU_ON_SET_UDC4),
968         wxAcceleratorEntry(wxACCEL_CTRL, '5', MENU_ON_SET_UDC5),
969         wxAcceleratorEntry(wxACCEL_CTRL, '6', MENU_ON_SET_UDC6),
970         wxAcceleratorEntry(wxACCEL_CTRL, '7', MENU_ON_SET_UDC7)
971     };
972 
973     wxAcceleratorTable tab(sizeof(entries)/sizeof(*entries), entries);
974     SetAcceleratorTable(tab);
975 
976     showDeletedTransactions_ = Model_Setting::instance().GetBoolSetting("SHOW_DELETED_TRANS", true);
977 
978     m_columns.push_back(std::make_tuple(" ", 25, wxLIST_FORMAT_LEFT));
979     m_columns.push_back(std::make_tuple(_("ID"), wxLIST_AUTOSIZE, wxLIST_FORMAT_LEFT));
980     m_columns.push_back(std::make_tuple(_("Date"), 112, wxLIST_FORMAT_LEFT));
981     m_columns.push_back(std::make_tuple(_("Number"), 70, wxLIST_FORMAT_LEFT));
982     m_columns.push_back(std::make_tuple(_("Payee"), 150, wxLIST_FORMAT_LEFT));
983     m_columns.push_back(std::make_tuple(_("Status"), wxLIST_AUTOSIZE_USEHEADER, wxLIST_FORMAT_LEFT));
984     m_columns.push_back(std::make_tuple(_("Category"), 150, wxLIST_FORMAT_LEFT));
985     m_columns.push_back(std::make_tuple(_("Withdrawal"), wxLIST_AUTOSIZE_USEHEADER, wxLIST_FORMAT_RIGHT));
986     m_columns.push_back(std::make_tuple(_("Deposit"), wxLIST_AUTOSIZE_USEHEADER, wxLIST_FORMAT_RIGHT));
987     m_columns.push_back(std::make_tuple(_("Balance"), wxLIST_AUTOSIZE_USEHEADER, wxLIST_FORMAT_RIGHT));
988     m_columns.push_back(std::make_tuple(_("Notes"), 250, wxLIST_FORMAT_LEFT));
989 
990     m_col_width = "CHECK_COL%d_WIDTH";
991 
992     m_default_sort_column = COL_DEF_SORT;
993 }
994 
~TransactionListCtrl()995 TransactionListCtrl::~TransactionListCtrl()
996 {
997 }
998 
999 //----------------------------------------------------------------------------
createColumns(mmListCtrl & lst)1000 void TransactionListCtrl::createColumns(mmListCtrl &lst)
1001 {
1002     int i = 0;
1003     for (const auto& entry : m_columns)
1004     {
1005         const wxString& heading = std::get<0>(entry);
1006         int width = Model_Setting::instance().GetIntSetting(wxString::Format(m_col_width, i), std::get<1>(entry));
1007         int format = std::get<2>(entry);
1008         lst.InsertColumn(i++, heading, format, width);
1009     }
1010 }
1011 
OnListItemSelected(wxListEvent & event)1012 void TransactionListCtrl::OnListItemSelected(wxListEvent& event)
1013 {
1014     m_selectedIndex = event.GetIndex();
1015     m_cp->updateExtraTransactionData(m_selectedIndex);
1016     topItemIndex_ = GetTopItem() + GetCountPerPage() - 1;
1017 
1018     if (GetSelectedItemCount()>1)
1019         m_cp->btnEdit_->Enable(false);
1020 
1021     m_selectedID = m_cp->m_trans[m_selectedIndex].TRANSID;
1022 }
1023 //----------------------------------------------------------------------------
1024 
OnListLeftClick(wxMouseEvent & event)1025 void TransactionListCtrl::OnListLeftClick(wxMouseEvent& event)
1026 {
1027     int Flags = wxLIST_HITTEST_ONITEM;
1028     long index = HitTest(wxPoint(event.m_x, event.m_y), Flags);
1029     if (index == -1)
1030     {
1031         m_selectedIndex = -1;
1032         m_cp->updateExtraTransactionData(m_selectedIndex);
1033     }
1034     event.Skip();
1035 }
1036 
OnMouseRightClick(wxMouseEvent & event)1037 void TransactionListCtrl::OnMouseRightClick(wxMouseEvent& event)
1038 {
1039     if (m_selectedIndex > -1)
1040         SetItemState(m_selectedIndex, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
1041     int Flags = wxLIST_HITTEST_ONITEM;
1042     m_selectedIndex = HitTest(wxPoint(event.m_x, event.m_y), Flags);
1043 
1044     if (m_selectedIndex >= 0)
1045     {
1046         SetItemState(m_selectedIndex, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1047         SetItemState(m_selectedIndex, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
1048     }
1049     m_cp->updateExtraTransactionData(m_selectedIndex);
1050 
1051     bool hide_menu_item = (m_selectedIndex < 0);
1052     bool type_transfer = false;
1053     bool have_category = false;
1054     if (m_selectedIndex > -1)
1055     {
1056         const Model_Checking::Full_Data& tran = m_cp->m_trans.at(m_selectedIndex);
1057         if (Model_Checking::type(tran.TRANSCODE) == Model_Checking::TRANSFER)
1058             type_transfer = true;
1059         if (!tran.has_split())
1060             have_category = true;
1061     }
1062     wxMenu menu;
1063     menu.Append(MENU_TREEPOPUP_NEW2, _("&New Transaction"));
1064 
1065     menu.AppendSeparator();
1066     menu.Append(MENU_TREEPOPUP_EDIT2, _("&Edit Transaction"));
1067     if (hide_menu_item) menu.Enable(MENU_TREEPOPUP_EDIT2, false);
1068 
1069     menu.Append(MENU_ON_COPY_TRANSACTION, _("&Copy Transaction"));
1070     if (hide_menu_item) menu.Enable(MENU_ON_COPY_TRANSACTION, false);
1071 
1072     menu.Append(MENU_ON_PASTE_TRANSACTION, _("&Paste Transaction"));
1073     if (m_selectedForCopy < 0) menu.Enable(MENU_ON_PASTE_TRANSACTION, false);
1074 
1075     menu.Append(MENU_ON_DUPLICATE_TRANSACTION, _("D&uplicate Transaction"));
1076     if (hide_menu_item) menu.Enable(MENU_ON_DUPLICATE_TRANSACTION, false);
1077 
1078     menu.Append(MENU_TREEPOPUP_MOVE2, _("&Move Transaction"));
1079     if (hide_menu_item || type_transfer || (Model_Account::checking_account_num() < 2))
1080         menu.Enable(MENU_TREEPOPUP_MOVE2, false);
1081 
1082     menu.AppendSeparator();
1083 
1084     menu.Append(MENU_TREEPOPUP_VIEW_SPLIT_CATEGORIES, _("&View Split Categories"));
1085     if (hide_menu_item || have_category)
1086         menu.Enable(MENU_TREEPOPUP_VIEW_SPLIT_CATEGORIES, false);
1087 
1088     menu.Append(MENU_TREEPOPUP_ORGANIZE_ATTACHMENTS, _("&Organize Attachments"));
1089     if (hide_menu_item)
1090         menu.Enable(MENU_TREEPOPUP_ORGANIZE_ATTACHMENTS, false);
1091 
1092     menu.Append(MENU_TREEPOPUP_CREATE_REOCCURANCE, _("Create Reoccuring T&ransaction"));
1093     if (hide_menu_item) menu.Enable(MENU_TREEPOPUP_CREATE_REOCCURANCE, false);
1094 
1095     menu.AppendSeparator();
1096 
1097     wxString menu_item_label = showDeletedTransactions_ ? _("Hide Deleted (Void)") : _("Show Deleted (Void)");
1098     menu.AppendCheckItem(MENU_TREEPOPUP_SHOWTRASH, menu_item_label);
1099 
1100     wxMenu* subGlobalOpMenuDelete = new wxMenu();
1101     subGlobalOpMenuDelete->Append(MENU_TREEPOPUP_DELETE2, _("&Delete Transaction"));
1102     if (hide_menu_item) subGlobalOpMenuDelete->Enable(MENU_TREEPOPUP_DELETE2, false);
1103     subGlobalOpMenuDelete->AppendSeparator();
1104     subGlobalOpMenuDelete->Append(MENU_TREEPOPUP_DELETE_VIEWED, _("Delete all transactions in current view"));
1105     subGlobalOpMenuDelete->Append(MENU_TREEPOPUP_DELETE_FLAGGED, _("Delete Viewed \"Follow Up\" Trans."));
1106     subGlobalOpMenuDelete->Append(MENU_TREEPOPUP_DELETE_UNRECONCILED, _("Delete Viewed \"Unreconciled\" Trans."));
1107     menu.Append(MENU_TREEPOPUP_DELETE2, _("&Delete "), subGlobalOpMenuDelete);
1108 
1109     menu.AppendSeparator();
1110 
1111     menu.Append(MENU_TREEPOPUP_MARKRECONCILED, _("Mark As &Reconciled"));
1112     if (hide_menu_item) menu.Enable(MENU_TREEPOPUP_MARKRECONCILED, false);
1113     menu.Append(MENU_TREEPOPUP_MARKUNRECONCILED, _("Mark As &Unreconciled"));
1114     if (hide_menu_item) menu.Enable(MENU_TREEPOPUP_MARKUNRECONCILED, false);
1115     menu.Append(MENU_TREEPOPUP_MARKVOID, _("Mark As &Void"));
1116     if (hide_menu_item) menu.Enable(MENU_TREEPOPUP_MARKVOID, false);
1117     menu.Append(MENU_TREEPOPUP_MARK_ADD_FLAG_FOLLOWUP, _("Mark For &Followup"));
1118     if (hide_menu_item) menu.Enable(MENU_TREEPOPUP_MARK_ADD_FLAG_FOLLOWUP, false);
1119     menu.Append(MENU_TREEPOPUP_MARKDUPLICATE, _("Mark As &Duplicate"));
1120     if (hide_menu_item) menu.Enable(MENU_TREEPOPUP_MARKDUPLICATE, false);
1121     menu.AppendSeparator();
1122 
1123     wxMenu* subGlobalOpMenu = new wxMenu();
1124     subGlobalOpMenu->Append(MENU_TREEPOPUP_MARKRECONCILED_ALL, _("as Reconciled"));
1125     subGlobalOpMenu->Append(MENU_TREEPOPUP_MARKUNRECONCILED_ALL, _("as Unreconciled"));
1126     subGlobalOpMenu->Append(MENU_TREEPOPUP_MARKVOID_ALL, _("as Void"));
1127     subGlobalOpMenu->Append(MENU_TREEPOPUP_MARK_ADD_FLAG_FOLLOWUP_ALL, _("as needing Followup"));
1128     subGlobalOpMenu->Append(MENU_TREEPOPUP_MARKDUPLICATE_ALL, _("as Duplicate"));
1129     menu.Append(MENU_SUBMENU_MARK_ALL, _("Mark all being viewed"), subGlobalOpMenu);
1130 
1131     PopupMenu(&menu, event.GetPosition());
1132     this->SetFocus();
1133 }
1134 //----------------------------------------------------------------------------
OnShowChbClick(wxCommandEvent &)1135 void TransactionListCtrl::OnShowChbClick(wxCommandEvent& /*event*/)
1136 {
1137     showDeletedTransactions_ = !showDeletedTransactions_;
1138     Model_Setting::instance().Set("SHOW_DELETED_TRANS", showDeletedTransactions_);
1139     refreshVisualList(m_selectedID);
1140 }
1141 
OnMarkTransaction(wxCommandEvent & event)1142 void TransactionListCtrl::OnMarkTransaction(wxCommandEvent& event)
1143 {
1144     int evt = event.GetId();
1145     wxString org_status = "";
1146     wxString status = "";
1147     if (evt == MENU_TREEPOPUP_MARKRECONCILED)              status = "R";
1148     else if (evt == MENU_TREEPOPUP_MARKUNRECONCILED)       status = "";
1149     else if (evt == MENU_TREEPOPUP_MARKVOID)               status = "V";
1150     else if (evt == MENU_TREEPOPUP_MARK_ADD_FLAG_FOLLOWUP) status = "F";
1151     else if (evt == MENU_TREEPOPUP_MARKDUPLICATE)          status = "D";
1152     else wxASSERT(false);
1153 
1154     Model_Checking::Data *trx = Model_Checking::instance().get(m_cp->m_trans[m_selectedIndex].TRANSID);
1155     if (trx)
1156     {
1157         org_status = trx->STATUS;
1158         m_cp->m_trans[m_selectedIndex].STATUS = status;
1159         trx->STATUS = status;
1160         Model_Checking::instance().save(trx);
1161     }
1162 
1163     bool bRefreshRequired = (status == "V") || (org_status == "V");
1164 
1165     if ((m_cp->transFilterActive_ && m_cp->transFilterDlg_->getStatusCheckBox()) || bRefreshRequired)
1166     {
1167         refreshVisualList(m_cp->m_trans[m_selectedIndex].TRANSID);
1168     }
1169     else
1170     {
1171         RefreshItems(m_selectedIndex, m_selectedIndex);
1172         m_cp->updateTable();
1173     }
1174 }
1175 //----------------------------------------------------------------------------
1176 
OnMarkAllTransactions(wxCommandEvent & event)1177 void TransactionListCtrl::OnMarkAllTransactions(wxCommandEvent& event)
1178 {
1179     int evt =  event.GetId();
1180     wxString status = "";
1181     if (evt ==  MENU_TREEPOPUP_MARKRECONCILED_ALL)             status = "R";
1182     else if (evt == MENU_TREEPOPUP_MARKUNRECONCILED_ALL)       status = "";
1183     else if (evt == MENU_TREEPOPUP_MARKVOID_ALL)               status = "V";
1184     else if (evt == MENU_TREEPOPUP_MARK_ADD_FLAG_FOLLOWUP_ALL) status = "F";
1185     else if (evt == MENU_TREEPOPUP_MARKDUPLICATE_ALL)          status = "D";
1186     else if (evt == MENU_TREEPOPUP_DELETE_VIEWED)              status = "X";
1187     else if (evt == MENU_TREEPOPUP_DELETE_FLAGGED)             status = "M";
1188     else if (evt == MENU_TREEPOPUP_DELETE_UNRECONCILED)        status = "0";
1189     else  wxASSERT(false);
1190 
1191     if (status == "X")
1192     {
1193         wxMessageDialog msgDlg(this
1194             ,_("Do you really want to delete all the transactions shown?")
1195             , _("Confirm Transaction Deletion")
1196             , wxYES_NO | wxNO_DEFAULT | wxICON_ERROR);
1197         if (msgDlg.ShowModal() == wxID_YES)
1198         {
1199             m_cp->DeleteViewedTransactions();
1200         }
1201     }
1202     else if (status == "M" || status == "0")
1203     {
1204         const wxString statusStr = (status == "M" ? _("Follow Up") : _("Unreconciled"));
1205         const wxString shotStatusStr = (status == "M" ? "F" : "");
1206         wxMessageDialog msgDlg(this
1207             ,wxString::Format(_("Do you really want to delete all the \"%s\" transactions shown?"), statusStr)
1208             , _("Confirm Transaction Deletion")
1209             , wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
1210         if (msgDlg.ShowModal() == wxID_YES)
1211         {
1212             m_cp->DeleteFlaggedTransactions(shotStatusStr);
1213         }
1214     }
1215     else
1216     {
1217         for (auto& tran : m_cp->m_trans)
1218         {
1219             tran.NOTES.Replace(mmAttachmentManage::GetAttachmentNoteSign(), wxEmptyString, true);
1220             tran.STATUS = status;
1221         }
1222         Model_Checking::instance().save(m_cp->m_trans);
1223     }
1224 
1225     refreshVisualList();
1226 }
1227 //----------------------------------------------------------------------------
1228 
OnColClick(wxListEvent & event)1229 void TransactionListCtrl::OnColClick(wxListEvent& event)
1230 {
1231     int ColumnNr;
1232     if (event.GetId() != MENU_HEADER_SORT)
1233         ColumnNr = event.GetColumn();
1234     else
1235         ColumnNr = m_ColumnHeaderNbr;
1236 
1237     if (0 > ColumnNr || ColumnNr >= COL_MAX || ColumnNr == COL_IMGSTATUS) return;
1238 
1239     /* Clear previous column image */
1240     setColumnImage(m_sortCol, -1);
1241 
1242 	if (g_sortcol == ColumnNr && event.GetId() != MENU_HEADER_SORT) m_asc = !m_asc; // toggle sort order
1243     g_asc = m_asc;
1244 
1245     m_sortCol = toEColumn(ColumnNr);
1246     g_sortcol = m_sortCol;
1247 
1248     setColumnImage(m_sortCol, m_asc ? ICON_ASC : ICON_DESC);
1249     Model_Setting::instance().Set("CHECK_ASC", (g_asc ? 1 : 0));
1250     Model_Setting::instance().Set("CHECK_SORT_COL", g_sortcol);
1251 
1252     refreshVisualList(m_selectedID, false);
1253 
1254 }
1255 //----------------------------------------------------------------------------
1256 
setColumnImage(EColumn col,int image)1257 void TransactionListCtrl::setColumnImage(EColumn col, int image)
1258 {
1259     wxListItem item;
1260     item.SetMask(wxLIST_MASK_IMAGE);
1261     item.SetImage(image);
1262 
1263     SetColumn(col, item);
1264 }
1265 //----------------------------------------------------------------------------
1266 
OnGetItemText(long item,long column) const1267 wxString TransactionListCtrl::OnGetItemText(long item, long column) const
1268 {
1269     return m_cp->getItem(item, column);
1270 }
1271 //----------------------------------------------------------------------------
1272 
1273 /*
1274     Returns the icon to be shown for each transaction for the required column
1275 */
OnGetItemColumnImage(long item,long column) const1276 int TransactionListCtrl::OnGetItemColumnImage(long item, long column) const
1277 {
1278     if (m_cp->m_trans.empty()) return ICON_NONE;
1279 
1280     int res = -1;
1281     if (column == COL_IMGSTATUS)
1282     {
1283         res = ICON_NONE;
1284         wxString status = m_cp->getItem(item, COL_STATUS);
1285         if ( status == "F")
1286             res = ICON_FOLLOWUP;
1287         else if (status == "R")
1288             res = ICON_RECONCILED;
1289         else if (status == "V")
1290             res = ICON_VOID;
1291         else if (status == "D")
1292             res = ICON_DUPLICATE;
1293     }
1294 
1295     return res;
1296 }
1297 //----------------------------------------------------------------------------
1298 
1299 /*
1300     Failed wxASSERT will hang application if active modal dialog presents on screen.
1301     Assertion's message box will be hidden until you press tab to activate one.
1302 */
OnGetItemAttr(long item) const1303 wxListItemAttr* TransactionListCtrl::OnGetItemAttr(long item) const
1304 {
1305     if (item < 0 || item >= (int)m_cp->m_trans.size()) return 0;
1306 
1307     const Model_Checking::Full_Data& tran = m_cp->m_trans[item];
1308     bool in_the_future = Model_Checking::TRANSDATE(&tran) > wxDateTime::Today();
1309 
1310     // apply alternating background pattern
1311     int user_colour_id = tran.FOLLOWUPID;
1312     if (user_colour_id < 0 ) user_colour_id = 0;
1313     else if (user_colour_id > 7) user_colour_id = 0;
1314 
1315     if (user_colour_id != 0)
1316     {
1317         if      (user_colour_id == 1) return (wxListItemAttr*)&m_attr11;
1318         else if (user_colour_id == 2) return (wxListItemAttr*)&m_attr12;
1319         else if (user_colour_id == 3) return (wxListItemAttr*)&m_attr13;
1320         else if (user_colour_id == 4) return (wxListItemAttr*)&m_attr14;
1321         else if (user_colour_id == 5) return (wxListItemAttr*)&m_attr15;
1322         else if (user_colour_id == 6) return (wxListItemAttr*)&m_attr16;
1323         else if (user_colour_id == 7) return (wxListItemAttr*)&m_attr17;
1324     }
1325     else if (in_the_future && item % 2) return (wxListItemAttr*)&m_attr3;
1326     else if (in_the_future)             return (wxListItemAttr*)&m_attr4;
1327     else if (item % 2)                  return (wxListItemAttr*)&m_attr1;
1328 
1329     return (wxListItemAttr*)&m_attr2;
1330 }
1331 //----------------------------------------------------------------------------
1332 // If any of these keys are encountered, the search for the event handler
1333 // should continue as these keys may be processed by the operating system.
OnChar(wxKeyEvent & event)1334 void TransactionListCtrl::OnChar(wxKeyEvent& event)
1335 {
1336 
1337     if (wxGetKeyState(WXK_ALT) ||
1338         wxGetKeyState(WXK_COMMAND) ||
1339         wxGetKeyState(WXK_UP) ||
1340         wxGetKeyState(WXK_DOWN) ||
1341         wxGetKeyState(WXK_LEFT) ||
1342         wxGetKeyState(WXK_RIGHT) ||
1343         wxGetKeyState(WXK_HOME) ||
1344         wxGetKeyState(WXK_END) ||
1345         wxGetKeyState(WXK_PAGEUP) ||
1346         wxGetKeyState(WXK_PAGEDOWN) ||
1347         wxGetKeyState(WXK_NUMPAD_UP) ||
1348         wxGetKeyState(WXK_NUMPAD_DOWN) ||
1349         wxGetKeyState(WXK_NUMPAD_LEFT) ||
1350         wxGetKeyState(WXK_NUMPAD_RIGHT) ||
1351         wxGetKeyState(WXK_NUMPAD_PAGEDOWN) ||
1352         wxGetKeyState(WXK_NUMPAD_PAGEUP) ||
1353         wxGetKeyState(WXK_NUMPAD_HOME) ||
1354         wxGetKeyState(WXK_NUMPAD_END) ||
1355         wxGetKeyState(WXK_DELETE) ||
1356         wxGetKeyState(WXK_NUMPAD_DELETE) ||
1357         wxGetKeyState(WXK_TAB)||
1358         wxGetKeyState(WXK_RETURN)||
1359         wxGetKeyState(WXK_NUMPAD_ENTER)||
1360         wxGetKeyState(WXK_SPACE)||
1361         wxGetKeyState(WXK_NUMPAD_SPACE)
1362         )
1363     {
1364         event.Skip();
1365     }
1366 }
1367 //----------------------------------------------------------------------------
1368 
OnCopy(wxCommandEvent & WXUNUSED (event))1369 void TransactionListCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
1370 {
1371     if (m_selectedIndex < 0) return;
1372 
1373     m_selectedForCopy = m_cp->m_trans[m_selectedIndex].TRANSID;
1374 }
1375 
OnDuplicateTransaction(wxCommandEvent & event)1376 void TransactionListCtrl::OnDuplicateTransaction(wxCommandEvent& event)
1377 {
1378     if (m_selectedIndex < 0) return;
1379 
1380     int transaction_id = m_cp->m_trans[m_selectedIndex].TRANSID;
1381     mmTransDialog dlg(this, m_cp->m_AccountID, transaction_id, true);
1382     if (dlg.ShowModal() == wxID_OK)
1383     {
1384         m_selectedIndex = dlg.getTransactionID();
1385         refreshVisualList(m_selectedIndex);
1386     }
1387     topItemIndex_ = GetTopItem() + GetCountPerPage() - 1;
1388 }
1389 
OnPaste(wxCommandEvent & WXUNUSED (event))1390 void TransactionListCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
1391 {
1392     if (m_selectedForCopy < 0) return;
1393     Model_Checking::Data* tran = Model_Checking::instance().get(m_selectedForCopy);
1394     if (tran)
1395     {
1396         int transactionID = OnPaste(tran);
1397         refreshVisualList(transactionID);
1398     }
1399 }
OnPaste(Model_Checking::Data * tran)1400 int TransactionListCtrl::OnPaste(Model_Checking::Data* tran)
1401 {
1402     bool useOriginalDate = Model_Setting::instance().GetBoolSetting(INIDB_USE_ORG_DATE_COPYPASTE, false);
1403 
1404     Model_Checking::Data* copy = Model_Checking::instance().clone(tran); //TODO: this function can't clone split transactions
1405     if (!useOriginalDate) copy->TRANSDATE = wxDateTime::Now().FormatISODate();
1406     if (Model_Checking::type(copy->TRANSCODE) != Model_Checking::TRANSFER) copy->ACCOUNTID = m_cp->m_AccountID;
1407     int transactionID = Model_Checking::instance().save(copy);
1408 
1409     Model_Splittransaction::Cache copy_split;
1410     for (const auto& split_item : Model_Checking::splittransaction(tran))
1411     {
1412         Model_Splittransaction::Data *copy_split_item = Model_Splittransaction::instance().clone(&split_item);
1413         copy_split_item->TRANSID = transactionID;
1414         copy_split.push_back(copy_split_item);
1415     }
1416     Model_Splittransaction::instance().save(copy_split);
1417 
1418     return transactionID;
1419 }
1420 
OnOpenAttachment(wxCommandEvent & event)1421 void TransactionListCtrl::OnOpenAttachment(wxCommandEvent& event)
1422 {
1423     if (m_selectedIndex < 0) return;
1424     int transaction_id = m_cp->m_trans[m_selectedIndex].TRANSID;
1425     wxString RefType = Model_Attachment::reftype_desc(Model_Attachment::TRANSACTION);
1426 
1427 	mmAttachmentManage::OpenAttachmentFromPanelIcon(this, RefType, transaction_id);
1428 	refreshVisualList(transaction_id);
1429 }
1430 
1431 //----------------------------------------------------------------------------
1432 
OnListKeyDown(wxListEvent & event)1433 void TransactionListCtrl::OnListKeyDown(wxListEvent& event)
1434 {
1435     if (wxGetKeyState(WXK_COMMAND) || wxGetKeyState(WXK_ALT) || wxGetKeyState(WXK_CONTROL)
1436         || m_selectedIndex == -1) {
1437         event.Skip();
1438         return;
1439     }
1440 
1441     topItemIndex_ = GetTopItem() + GetCountPerPage() -1;
1442 
1443     //Read status of the selected transaction
1444     wxString status = m_cp->m_trans[m_selectedIndex].STATUS;
1445 
1446     if (wxGetKeyState(wxKeyCode('R')) && status != "R") {
1447         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, MENU_TREEPOPUP_MARKRECONCILED);
1448         OnMarkTransaction(evt);
1449     }
1450     else if (wxGetKeyState(wxKeyCode('U')) && status != "") {
1451         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, MENU_TREEPOPUP_MARKUNRECONCILED);
1452         OnMarkTransaction(evt);
1453     }
1454     else if (wxGetKeyState(wxKeyCode('F')) && status != "F") {
1455         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, MENU_TREEPOPUP_MARK_ADD_FLAG_FOLLOWUP);
1456         OnMarkTransaction(evt);
1457     }
1458     else if (wxGetKeyState(wxKeyCode('D')) && status != "D") {
1459         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, MENU_TREEPOPUP_MARKDUPLICATE);
1460         OnMarkTransaction(evt);
1461     }
1462     else if (wxGetKeyState(wxKeyCode('V')) && status != "V") {
1463         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, MENU_TREEPOPUP_MARKVOID);
1464         OnMarkTransaction(evt);
1465     }
1466     else if ((wxGetKeyState(WXK_DELETE) || wxGetKeyState(WXK_NUMPAD_DELETE)) && status != "V")
1467     {
1468         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, MENU_TREEPOPUP_MARKVOID);
1469         OnMarkTransaction(evt);
1470     }
1471     else if (wxGetKeyState(WXK_DELETE) || wxGetKeyState(WXK_NUMPAD_DELETE))
1472     {
1473         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, MENU_TREEPOPUP_DELETE2);
1474         OnDeleteTransaction(evt);
1475     }
1476     else {
1477         event.Skip();
1478         return;
1479     }
1480 }
1481 //----------------------------------------------------------------------------
1482 
OnDeleteTransaction(wxCommandEvent &)1483 void TransactionListCtrl::OnDeleteTransaction(wxCommandEvent& /*event*/)
1484 {
1485     //check if a transaction is selected
1486     if (GetSelectedItemCount() < 1) return;
1487 
1488     topItemIndex_ = GetTopItem() + GetCountPerPage() -1;
1489 
1490     //ask if they really want to delete
1491     wxMessageDialog msgDlg(this
1492         , _("Do you really want to delete the selected transaction?")
1493         , _("Confirm Transaction Deletion")
1494         , wxYES_NO | wxYES_DEFAULT | wxICON_ERROR);
1495 
1496     if (msgDlg.ShowModal() == wxID_YES)
1497     {
1498         long x = 0;
1499         for (const auto& i : m_cp->m_trans)
1500         {
1501             long transID = i.TRANSID;
1502             if (GetItemState(x, wxLIST_STATE_SELECTED) == wxLIST_STATE_SELECTED)
1503             {
1504                 // remove also removes any split transactions
1505                 Model_Checking::instance().remove(transID);
1506                 mmAttachmentManage::DeleteAllAttachments(Model_Attachment::reftype_desc(Model_Attachment::TRANSACTION), transID);
1507                 if (x <= topItemIndex_) topItemIndex_--;
1508                 if (!m_cp->m_trans.empty() && m_selectedIndex > 0) m_selectedIndex--;
1509                 if (m_selectedForCopy == transID) m_selectedForCopy = -1;
1510             }
1511             x++;
1512         }
1513 
1514         refreshVisualList();
1515     }
1516 }
1517 //----------------------------------------------------------------------------
1518 
OnEditTransaction(wxCommandEvent &)1519 void TransactionListCtrl::OnEditTransaction(wxCommandEvent& /*event*/)
1520 {
1521     if (m_selectedIndex < 0) return;
1522 
1523     int transaction_id = m_cp->m_trans[m_selectedIndex].TRANSID;
1524     mmTransDialog dlg(this, m_cp->m_AccountID, transaction_id);
1525     if (dlg.ShowModal() == wxID_OK)
1526     {
1527         refreshVisualList(transaction_id);
1528     }
1529     topItemIndex_ = GetTopItem() + GetCountPerPage() - 1;
1530 }
1531 
OnNewTransaction(wxCommandEvent &)1532 void TransactionListCtrl::OnNewTransaction(wxCommandEvent& /*event*/)
1533 {
1534     mmTransDialog dlg(this, m_cp->m_AccountID, 0);
1535     if (dlg.ShowModal() == wxID_OK)
1536     {
1537         m_cp->mmPlayTransactionSound();
1538         refreshVisualList(dlg.getTransactionID());
1539     }
1540 }
1541 //----------------------------------------------------------------------------
1542 
OnSetUserColour(wxCommandEvent & event)1543 void TransactionListCtrl::OnSetUserColour(wxCommandEvent& event)
1544 {
1545     int user_colour_id = event.GetId();
1546     user_colour_id -= MENU_ON_SET_UDC0;
1547 
1548     Model_Checking::Data * transaction = Model_Checking::instance().get(m_selectedID);
1549     if (transaction)
1550     {
1551         transaction->FOLLOWUPID = user_colour_id;
1552         Model_Checking::instance().save(transaction);
1553         refreshVisualList(m_selectedID);
1554     }
1555 }
1556 //----------------------------------------------------------------------------
1557 
refreshVisualList(int trans_id,bool filter)1558 void TransactionListCtrl::refreshVisualList(int trans_id, bool filter)
1559 {
1560     this->SetEvtHandlerEnabled(false);
1561     Hide();
1562 
1563     // decide whether top or down icon needs to be shown
1564     setColumnImage(g_sortcol, g_asc ? ICON_ASC : ICON_DESC);
1565     if (filter) m_cp->filterTable();
1566     SetItemCount(m_cp->m_trans.size());
1567     Show();
1568     m_cp->sortTable();
1569     m_cp->markSelectedTransaction(trans_id);
1570 
1571     if (topItemIndex_ >= (long)m_cp->m_trans.size())
1572         topItemIndex_ = g_asc ? (long)m_cp->m_trans.size() - 1 : 0;
1573     if (m_selectedIndex > (long)m_cp->m_trans.size() - 1) m_selectedIndex = -1;
1574     if (topItemIndex_ < m_selectedIndex) topItemIndex_ = m_selectedIndex;
1575 
1576 
1577     if (m_selectedIndex >= 0 && !m_cp->m_trans.empty())
1578     {
1579         SetItemState(m_selectedIndex, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1580         SetItemState(m_selectedIndex, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
1581         if (topItemIndex_ < 0 || (topItemIndex_ - m_selectedIndex) > GetCountPerPage())
1582             topItemIndex_ = m_selectedIndex;
1583         EnsureVisible(topItemIndex_);
1584     }
1585 
1586     m_cp->setAccountSummary();
1587     m_cp->updateExtraTransactionData(m_selectedIndex);
1588     this->SetEvtHandlerEnabled(true);
1589     Refresh();
1590     Update();
1591     m_cp->m_listCtrlAccount->SetFocus();
1592 }
1593 
OnMoveTransaction(wxCommandEvent &)1594 void TransactionListCtrl::OnMoveTransaction(wxCommandEvent& /*event*/)
1595 {
1596     if (m_selectedIndex < 0) return;
1597 
1598     const Model_Account::Data* source_account = Model_Account::instance().get(m_cp->m_AccountID);
1599     wxString source_name = source_account->ACCOUNTNAME;
1600     wxString headerMsg = wxString::Format(_("Moving Transaction from %s to..."), source_name);
1601 
1602     mmSingleChoiceDialog scd(this
1603         , _("Select the destination Account ")
1604         , headerMsg
1605         , Model_Account::instance().all_checking_account_names());
1606 
1607     if (scd.ShowModal() == wxID_OK)
1608     {
1609         int dest_account_id = -1;
1610         wxString dest_account_name = scd.GetStringSelection();
1611         Model_Account::Data* dest_account = Model_Account::instance().get(dest_account_name);
1612         if (dest_account)
1613             dest_account_id = dest_account->ACCOUNTID;
1614         else
1615             return;
1616 
1617         Model_Checking::Full_Data& tran = m_cp->m_trans[m_selectedIndex];
1618         tran.ACCOUNTID = dest_account_id;
1619         Model_Checking::instance().save(&tran);
1620         refreshVisualList();
1621     }
1622 }
1623 
1624 //----------------------------------------------------------------------------
OnViewSplitTransaction(wxCommandEvent &)1625 void TransactionListCtrl::OnViewSplitTransaction(wxCommandEvent& /*event*/)
1626 {
1627     if (m_selectedIndex > -1) {
1628         const Model_Checking::Full_Data& tran = m_cp->m_trans.at(m_selectedIndex);
1629         if (tran.has_split())
1630             m_cp->DisplaySplitCategories(tran.TRANSID);
1631     }
1632 }
1633 
1634 //----------------------------------------------------------------------------
OnOrganizeAttachments(wxCommandEvent &)1635 void TransactionListCtrl::OnOrganizeAttachments(wxCommandEvent& /*event*/)
1636 {
1637     if (m_selectedIndex < 0) return;
1638 
1639     wxString RefType = Model_Attachment::reftype_desc(Model_Attachment::TRANSACTION);
1640     int RefId = m_cp->m_trans[m_selectedIndex].TRANSID;
1641 
1642     mmAttachmentDialog dlg(this, RefType, RefId);
1643     dlg.ShowModal();
1644 
1645 	refreshVisualList(RefId);
1646 }
1647 
1648 //----------------------------------------------------------------------------
OnCreateReoccurance(wxCommandEvent &)1649 void TransactionListCtrl::OnCreateReoccurance(wxCommandEvent& /*event*/)
1650 {
1651     if (m_selectedIndex < 0) return;
1652 
1653     mmBDDialog dlg(this, 0, false, false);
1654     dlg.SetDialogParameters(m_cp->m_trans[m_selectedIndex]);
1655     if (dlg.ShowModal() == wxID_OK)
1656     {
1657         wxMessageBox(_("Reoccuring Transaction saved."));
1658     }
1659 }
1660 
1661 //----------------------------------------------------------------------------
OnListItemActivated(wxListEvent &)1662 void TransactionListCtrl::OnListItemActivated(wxListEvent& /*event*/)
1663 {
1664     if (m_selectedIndex < 0) return;
1665 
1666     wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, MENU_TREEPOPUP_EDIT2);
1667     AddPendingEvent(evt);
1668 }
1669 
1670