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