GnuCashew ~ Web Application compatible with GnuCash sql data files.
GCW
Loading...
Searching...
No Matches
AccountsTreeView.cpp
Go to the documentation of this file.
1#line 2 "src/Gui/AccountsTreeView.cpp"
2
3//#define EDIT_FORM_AS_POPUP_DIALOG
4#define EDIT_FORM_AS_SPLIT_PAGE
5
6
7#include <any>
8
9#include <Wt/Json/Array.h>
10#include <Wt/Json/Parser.h>
11#include <Wt/Json/Serializer.h>
12#include <Wt/WText.h>
13#include <Wt/WTreeTableNode.h>
14#include <Wt/WMessageBox.h>
15
16#include "../define.h"
17#include "AccountEditor.h"
18#include "AccountsTreeView.h"
19
21AccountsTreeView( const std::string & _selectedAccountGuid, int _columnCount )
22: m_columnCount( _columnCount )
23{
24 init();
25
27
28} // endAccountsTreeView( const std::string & _selectedAccountGuid, int _columnCount )
29
31AccountsTreeView( int _columnCount )
32: m_columnCount( _columnCount )
33{
34 init();
35
36} // endAccountsTreeView( int _columnCount )
37
38auto
40init()-> void
41{
42 addStyleClass( "AccountsTreeView" );
43
44 m_gridLayout = setLayout( std::make_unique< Wt::WGridLayout >() );
45// lw-> setSpacing( 0 );
46
47 m_view = m_gridLayout-> addWidget( std::make_unique< Wt::WTreeView >(), 0, 0 );
48
49 view()-> setSelectionBehavior( Wt::SelectionBehavior::Rows );
50 view()-> setSelectionMode( Wt::SelectionMode::Single );
51 view()-> setAlternatingRowColors( true );
52 view()-> doubleClicked().connect( this, &AccountsTreeView::on_doubleClicked );
53 view()-> setAttributeValue( "oncontextmenu","event.cancelBubble=true;event.returnValue=false;return false;" );
54 view()-> mouseWentUp().connect( this, &AccountsTreeView::on_showPopup_triggered );
55
56 std::vector< std::string > cols =
57 {
58 "accountcode" ,
59 "accountcolor" ,
60 "accountname" ,
61 "balance" ,
62 "balancelimit" ,
63 "balanceperiod" ,
64 "balanceusd" ,
65 "cleared" ,
66 "clearedusd" ,
67 "commodity" ,
68 "description" ,
69 "futureminimum" ,
70 "futureminimumusd" ,
71 "hidden" ,
72 "lastnum" ,
73 "lastreconciledate" ,
74 "notes" ,
75 "openingbalance" ,
76 "placeholder" ,
77 "present" ,
78 "presentusd" ,
79 "reconciled" ,
80 "reconciledusd" ,
81 "taxinfo" ,
82 "total" ,
83 "totalperiod" ,
84 "totalusd" ,
85 "type"
86 };
87
88 for( int i=0; i< m_columnCount; i++ )
89 m_columns.push_back( TR8( "gcw.AccountsTreeView.column." + cols.at(i) ) );
90
91 setModel();
92
93 loadConfig();
94
95 view()-> collapsed ().connect( this, &AccountsTreeView::saveConfig );
96 view()-> expanded ().connect( this, &AccountsTreeView::saveConfig );
97 view()-> selectionChanged ().connect( this, &AccountsTreeView::saveConfig );
98
99} // endinit()-> void
100
101auto
103on_showPopup_triggered( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )-> void
104{
105 if( _event.button() == Wt::MouseButton::Right )
106 {
107 std::cout << __FILE__ << ":" << __LINE__ << " right-click pop-up menu" << std::endl;
108
109#ifdef NEVER
110 /*
111 ** Set up the items in the pop-up menu
112 **
113 */
114 while( m_popup.count() )
115 m_popup.removeItem( m_popup.itemAt(0) );
116
117 m_popup.addItem("icons/folder_new.gif", "Create a New Folder", this, &WEB::FolderTableView::on_pbAddFolder_triggered );
118 m_popup.addItem("Rename", this, &WEB::FolderTableView::on_pbRenameItem_triggered ); //->setCheckable(true);
119
120 std::string fileName = Wt::asString(m_tableView.model()-> data( index.row(), 0 )).toUTF8();
121 COUT_( fileName );
122
123 if( isFolder(fileName) )
124 m_popup.addItem("Delete this Folder", this, &WEB::FolderTableView::on_pbDeleteItem_triggered );
125 else
126 m_popup.addItem("Delete this File", this, &WEB::FolderTableView::on_pbDeleteItem_triggered );
127
128 m_popup.addSeparator();
129 m_popup.addItem("Upload a file", this, &WEB::FolderTableView::on_pbUploadFile_triggered );
130 m_popup.addItem("Refresh", this, &WEB::FolderTableView::refresh );
131 m_popup.addSeparator();
132 m_popup.addItem("Properties", this, &WEB::FolderTableView::on_pbProperties_triggered );
133
134 // Select the item, if it was not yet selected.
135 if( !m_tableView.isSelected(index) )
136 {
137 m_tableView.clearSelection();
138 m_tableView.select(index);
139 }
140
141 if( m_popup.isHidden() )
142 {
143 m_popup.popup(event);
144 }
145 else
146 {
147 m_popup.hide();
148 }
149
150#endif
151
152 }
153
154} // endon_showPopup_triggered( Wt::WModelIndex _index, Wt::WMouseEvent _event )
155
156
157/*!
158** \return GUID String
159*/
160std::string
162selectedAccount() const
163{
164 std::string retVal;
165
166 /*
167 ** The tree-view should have only one selection. Grab
168 ** its index and get to the User data that carries the
169 ** GUID of the selected item.
170 **
171 */
172 auto selected = view()-> selectedIndexes();
173 if( selected.size() == 1 )
174 retVal =
176 (
177 model()-> data( *selected.begin(),
179 )
180 ).toUTF8();
181
182 return retVal;
183
184} // endstd::string GCW::Gui::AccountsTreeView::selectedAccount()
185
186void
188editAccount( const std::string & _accountGuid )
189{
190 if( _accountGuid == "" )
191 return;
192
193#ifdef EDIT_FORM_AS_POPUP_DIALOG
194 GCW::Gui::AccountEditorDialog dialog( "Edit Account" );
195 dialog.exec();
196#endif
197
198#ifdef EDIT_FORM_AS_SPLIT_PAGE
199
200 /*
201 **
202 **
203 */
204 if( m_editAccountWidget )
205 {
206// m_gridLayout-> removeWidget( m_editWidget );
207// m_editWidget = nullptr;
208 Wt::WMessageBox::show( "AccountsTree", "Please close the account you are editing", Wt::StandardButton::Ok );
209 return;
210 }
211
212 /*
213 ** Split the page to open/edit this item
214 **
215 */
216 m_editAccountWidget = m_gridLayout-> addWidget( std::make_unique< GCW::Gui::AccountEditor >( _accountGuid ), 0, 1 );
217 m_gridLayout-> setColumnResizable( 0, true, "25%" );
218
219 m_editAccountWidget->
220 save().connect( [=]()
221 {
222// refreshViews();
223 m_gridLayout-> removeWidget( m_editAccountWidget );
224 m_editAccountWidget = nullptr;
225 });
226
227 m_editAccountWidget->
228 cancel().connect( [=]()
229 {
230// refreshViews();
231 m_gridLayout-> removeWidget( m_editAccountWidget );
232 m_editAccountWidget = nullptr;
233 });
234
235#endif
236
237} // endvoid GCW::Gui::AccountsTreeView::editAccount( const std::string & _accountGuid )
238
239
240void
243{
244 editAccount( selectedAccount() );
245
246} // endvoid GCW::Gui::AccountsTreeView::editAccount( const std::string & _accountGuid )
247
248
249void
251setModel()
252{
253 m_model = std::make_shared< Model >();
254
255 m_model-> load( m_columnCount );
256
257 view()-> setModel( m_model );
258
259 view()-> sortByColumn( 0, Wt::SortOrder::Ascending );
260
261} // endvoid GCW::Gui::AccountsTreeView::setModel()
262
266{
268
269 if( GCW::app()-> gnucashew_session().hasGnuCashewExtensions() )
270 {
271 Wt::Dbo::Transaction t( GCW::app()-> gnucashew_session() );
272
273 retVal = GCW::Dbo::Vars::get( "config", "AccountsTreeView" );
274 }
275
276 return retVal;
277
278} // endconfigItem()
279
280
281void
284{
285 if( !GCW::app()-> gnucashew_session().hasGnuCashewExtensions() )
286 return;
287
288 Wt::Dbo::Transaction t( GCW::app()-> gnucashew_session() );
289 configItem().modify()-> setVarField( Wt::Json::serialize( toJson() ) );
290
291} // endsaveConfig()
292
293void
296{
297 if( !GCW::app()-> gnucashew_session().hasGnuCashewExtensions() )
298 return;
299
300 Wt::Json::Object jobj;
301 try {
302 Wt::Json::parse( configItem()-> varField(), jobj );
303 }
304 catch( std::exception & e )
305 {
306// std::cout << __FILE__ << ":" << __LINE__ << " " << e.what() << std::endl;
307 }
308
309 fromJson( jobj );
310
311} // endloadConfig()
312
313/*
314** This will iterate a single a WTreeView and fill
315** a vector of every node which is the .last-expanded.
316** node of every branch.
317**
318*/
319bool
321iterate( Wt::Json::Array & _jary, Wt::WModelIndex _parent ) const
322{
323 /*
324 ** If this _parent node is not expanded, then we're basically done.
325 **
326 */
327 if( !view()-> isExpanded( _parent ) )
328 return false;
329
330 /*
331 ** This _parent node is expanded, so loop through all the
332 ** child nodes checking if any of them are expanded.
333 **
334 */
335 bool expanded = false;
336 for( int row=0; row< view()-> model()-> rowCount( _parent ); row++ )
337 expanded |= iterate( _jary, view()-> model()-> index( row, 0, _parent ) );
338
339 /*
340 ** None of the child nodes are expanded, so record this _parent
341 ** node as the 'last' node in the tree
342 **
343 */
344 if( !expanded )
345 {
346 /*
347 ** The true root node is not associated with an actual account,
348 ** it is simply the invisibleRoot of the tree itself, and only
349 ** contains the set of first-root nodes that actually get
350 ** displayed. So, there is no User data in this one, don't record it.
351 **
352 */
353 auto accountGuid = Wt::asString( _parent.data( Wt::ItemDataRole::User ) );
354 if( accountGuid != "" )
355 _jary.push_back( accountGuid );
356
357 } // endif( !expanded )
358
359 /*
360 ** Something is expanded. Either we are expanded, or
361 ** one of the sub-nodes are expanded, so return that 'someone' is
362 ** expanded.
363 **
364 */
365 return true;
366
367} // endvoid iterate( Wt::WModelIndex _index ) const
368
369
372toJson() const
373{
374 Wt::Json::Object jobj;
375
376 jobj["selected"] = Wt::WString( selectedAccount() );
377
378 for( int col=0; col< 7; col++ )
379 jobj[ Wt::WString("cw{1}").arg( col ).toUTF8() ] = Wt::WString( view()-> columnWidth( col ).cssText() );
380
381 Wt::Json::Array jary;
382 iterate( jary );
383 jobj["expanded"] = jary;
384
385 return jobj;
386
387}
388
389bool
391expandNode( const std::string & _accountGuid, Wt::WModelIndex _parent )
392{
393 bool retVal = false;
394
395 /*
396 ** Loop through all the children in this node
397 **
398 */
399// Wt::Dbo::Transaction t( GCW::app()-> gnucashew_session() );
400 for( int row=0; row< view()-> model()-> rowCount( _parent ); row++ )
401 {
402 /*
403 ** get the index for this child of this node
404 */
405 auto child = view()-> model()-> index( row, 0, _parent );
406
407 /*
408 ** get the guid of this child
409 */
410 auto nodeGuid = Wt::asString( child.data( Wt::ItemDataRole::User ) ).toUTF8();
411
412 /*
413 ** if this node matches the account, expand it and set the
414 ** return to 'found'
415 **
416 */
417 if( nodeGuid == _accountGuid )
418 {
419 view()-> expand( child );
420 retVal |= true;
421 }
422
423 /*
424 ** remember if any of the sub-nodes get expanded.
425 **
426 */
427 retVal |= expandNode( _accountGuid, child );
428
429 } // endfor( int row=0; row< view()-> model()-> rowCount( _parent ); row++ )
430
431 /*
432 ** Either this node was expanded, or any one of
433 ** the child nodes was expanded, so therefore we
434 ** need to also expand this node.
435 **
436 */
437 if( retVal )
438 view()-> expand( _parent );
439
440 /*
441 ** None of the nodes here got expanded
442 **
443 */
444 return retVal;
445
446} // endexpandNode( const std::string & _accountGuid, Wt::WModelIndex _parent )
447
448bool
451{
452 auto jary = _jobj.get("expanded").orIfNull( Wt::Json::Array() );
453
454 for( auto value : jary )
455 expandNode( value.orIfNull( "" ) );
456
457 return true;
458
459} // endexpandNodes()
460
461/*!
462** \brief Find Index by AccountGuid
463**
464** This will loop through the tree and locate a specific
465** index by it's accountGuid value.
466**
467*/
470findIndex( const std::string & _accountGuid, Wt::WModelIndex _parentIndex )
471{
472 /*
473 ** If this is the index we are looking for, then just return it.
474 **
475 */
476 if( Wt::asString( _parentIndex.data( Wt::ItemDataRole::User ) ) == _accountGuid )
477 return _parentIndex;
478
479 /*
480 ** Loop through all the child nodes checking them for
481 ** matches
482 **
483 */
484 for( int row=0; row< view()-> model()-> rowCount( _parentIndex ); row++ )
485 {
486 auto childIndex = findIndex( _accountGuid, view()-> model()-> index( row, 0, _parentIndex ) );
487
488 /*
489 ** If we get back a valid index, then we have what we
490 ** need and can just return it.
491 **
492 */
493 if( childIndex.isValid() )
494 return childIndex;
495
496 } // endfor( int row=0; row< view()-> model()-> rowCount( _parentIndex ); row++ )
497
498 /*
499 ** Return an invalid index indicating not-found.
500 **
501 */
502 return Wt::WModelIndex();
503
504} // endfindIndex( const std::string & _accountGuid, Wt::WModelIndex _parentIndex )
505
506bool
508setSelected( const std::string & _accountGuid )
509{
510 auto index = findIndex( _accountGuid );
511
512 view()-> select ( index );
513 view()-> scrollTo ( index, Wt::ScrollHint::PositionAtCenter );
514
515 return true;
516
517} // endexpandNodes()
518
519bool
521fromJson( Wt::Json::Object & _jobj )
522{
523 expandTreeNodes( _jobj );
524
525 setSelected( _jobj.get("selected").orIfNull( std::string() ) );
526
527 return true;
528
529} // endfromJson( const Wt::Json::Object & _jobj )
530
531void
533test()
534{
535 std::cout << __FILE__ << ":" << __LINE__ << " " << std::endl;
536
537 std::cout << __FILE__ << ":" << __LINE__ << " " << Wt::Json::serialize( toJson() ) << std::endl;
538
539} // endvoid GCW::Gui::AccountsTreeView::test()
540
541
542void
544on_doubleClicked( const Wt::WModelIndex & index, const Wt::WMouseEvent & event )
545{
546#ifdef NEVER
547 std::cout << std::endl << std::endl << __FILE__ << ":" << __LINE__
548 << " " << "on_doubleClicked:"
549 << " " << index.row()
550 << " " << Wt::asString( m_model-> data( index, Wt::ItemDataRole::User ) )
551 << std::endl << std::endl
552 << std::endl;
553#endif
554
555 /*
556 ** The 'model->data::User' element should return the guid of the account
557 **
558 */
559 m_doubleClicked.emit( Wt::asString( m_model-> data( index, Wt::ItemDataRole::User ) ).toUTF8() );
560
561} // endvoid GCW::Gui::AccountsTreeView::on_doubleClicked( const Wt::WModelIndex & index, const Wt::WMouseEvent & event )
562
563
static bool iterate(Wt::Json::Array &_jary, Wt::WModelIndex _parent)
Definition Core.cpp:95
static std::unique_ptr< GCW::Gui::BillPay::PaymentWidgetDialog > dialog
AccountsTreeView(int _columnCount)
auto toJson() const -> Wt::Json::Object
auto on_doubleClicked(const Wt::WModelIndex &index, const Wt::WMouseEvent &event) -> void
auto expandNode(const std::string &_accountGuid, Wt::WModelIndex _parent=Wt::WModelIndex()) -> bool
auto configItem() -> GCW::Dbo::Vars::Item::Ptr
Config Item.
auto expandTreeNodes(Wt::Json::Object &_jobj) -> bool
auto findIndex(const std::string &_accountGuid, Wt::WModelIndex _parentIndex=Wt::WModelIndex()) -> Wt::WModelIndex
Find Index by AccountGuid.
auto on_showPopup_triggered(const Wt::WModelIndex &_index, const Wt::WMouseEvent &_event) -> void
auto iterate(Wt::Json::Array &_jary, Wt::WModelIndex _parent=Wt::WModelIndex()) const -> bool
auto fromJson(Wt::Json::Object &_jobj) -> bool
auto setSelected(const std::string &_accountGuid) -> bool
auto selectedAccount() const -> std::string
auto editAccount(const std::string &_accountGuid) -> void
static constexpr const int User
const Value & get(const std::string &name) const
const WString & orIfNull(const WString &v) const
Widget * addNew(Args &&...args)
int row() const
cpp17::any data(ItemDataRole role=ItemDataRole::Display) const
std::string toUTF8() const
WString & arg(const std::wstring &value)
#define TR8(X)
Definition define.h:18
void parse(const std::string &input, Value &result, bool validateUTF8=true)
std::string serialize(const Object &obj, int indentation=1)
WString asString(const cpp17::any &v, const WString &formatString=WString())
auto get(const std::string &_keyValue, const std::string &_cfyValue="*", bool _add=true) -> GCW::Dbo::Vars::Item::Ptr
Definition Vars.cpp:16
App * app()
Definition App.cpp:75