GnuCashew ~ Web Application compatible with GnuCash sql data files.
GCW
Loading...
Searching...
No Matches
TableView.cpp
Go to the documentation of this file.
1#line 2 "src/Gui/TableView.cpp"
2
3//#define TABLEVIEW_HACK_V4_12_16
4
5#ifdef TABLEVIEW_HACK_V4_12_16
6#include <ctime>
7#endif
8
9#include <Wt/WMessageBox.h>
10#include <Wt/WStandardItem.h>
11#include <Wt/WStandardItemModel.h>
12
13#include "../Glb/gcwglobal.h"
14#include "../GnuCashew.h"
15#include "TableView.h"
16
17// Pixels
23
24namespace {
25static auto keyToCode( Wt::Key _key )-> std::string
26{
27 std::map< Wt::Key, std::string > keyMap =
28 {
29 { Wt::Key::Unknown , "Unknown" },
30 { Wt::Key::Enter , "Enter" },
31 { Wt::Key::Tab , "Tab" },
32 { Wt::Key::Backspace , "Backspace" },
33 { Wt::Key::Shift , "Shift" },
34 { Wt::Key::Control , "Control" },
35 { Wt::Key::Alt , "Alt" },
36 { Wt::Key::PageUp , "Page up" },
37 { Wt::Key::PageDown , "Page down" },
38 { Wt::Key::End , "End" },
39 { Wt::Key::Home , "Home" },
40 { Wt::Key::Left , "Left" },
41 { Wt::Key::Up , "Up" },
42 { Wt::Key::Right , "Right" },
43 { Wt::Key::Down , "Down" },
44 { Wt::Key::Insert , "Insert" },
45 { Wt::Key::Delete , "Delete" },
46 { Wt::Key::Escape , "Escape" },
47 { Wt::Key::F1 , "F1" },
48 { Wt::Key::F2 , "F2" },
49 { Wt::Key::F3 , "F3" },
50 { Wt::Key::F4 , "F4" },
51 { Wt::Key::F5 , "F5" },
52 { Wt::Key::F6 , "F6" },
53 { Wt::Key::F7 , "F7" },
54 { Wt::Key::F8 , "F8" },
55 { Wt::Key::F9 , "F9" },
56 { Wt::Key::F10 , "F10" },
57 { Wt::Key::F11 , "F11" },
58 { Wt::Key::F12 , "F12" },
59 { Wt::Key::Space , "Space" },
60 { Wt::Key::A , "'A'" },
61 { Wt::Key::B , "'B'" },
62 { Wt::Key::C , "'C'" },
63 { Wt::Key::D , "'D'" },
64 { Wt::Key::E , "'E'" },
65 { Wt::Key::F , "'F'" },
66 { Wt::Key::G , "'G'" },
67 { Wt::Key::H , "'H'" },
68 { Wt::Key::I , "'I'" },
69 { Wt::Key::J , "'J'" },
70 { Wt::Key::K , "'K'" },
71 { Wt::Key::L , "'L'" },
72 { Wt::Key::M , "'M'" },
73 { Wt::Key::N , "'N'" },
74 { Wt::Key::O , "'O'" },
75 { Wt::Key::P , "'P'" },
76 { Wt::Key::Q , "'Q'" },
77 { Wt::Key::R , "'R'" },
78 { Wt::Key::S , "'S'" },
79 { Wt::Key::T , "'T'" },
80 { Wt::Key::U , "'U'" },
81 { Wt::Key::V , "'V'" },
82 { Wt::Key::W , "'W'" },
83 { Wt::Key::X , "'X'" },
84 { Wt::Key::Y , "'Y'" },
85 { Wt::Key::Z , "'Z'" },
86 { Wt::Key::Key_1 , "'1'" },
87 { Wt::Key::Key_2 , "'2'" },
88 { Wt::Key::Key_3 , "'3'" },
89 { Wt::Key::Key_4 , "'4'" },
90 { Wt::Key::Key_5 , "'5'" },
91 { Wt::Key::Key_6 , "'6'" },
92 { Wt::Key::Key_7 , "'7'" },
93 { Wt::Key::Key_8 , "'8'" },
94 { Wt::Key::Key_9 , "'9'" },
95 { Wt::Key::Key_0 , "'0'" }
96 }; // endstd::map< Wt::Key, std::string > keyMap =
97
98 return keyMap[_key];
99
100}; // endstatic auto keyToCode( Wt::Key _key )-> std::string
101
102} // endnamespace {
103
104
106TableView()
107: Wt::WTableView()
108{
109 addStyleClass( "Gcw-TableView" );
110
111 /*
112 ** To support right-click, we need to disable the built-in browser
113 ** context menu.
114 **
115 ** Note that disabling the context menu and catching the
116 ** right-click does not work reliably on all browsers.
117 */
118#ifdef NEVER
120 (
121 "oncontextmenu",
122 "event.cancelBubble = true; event.returnValue = false; return false;"
123 );
124#endif
125
128 setLayoutSizeAware( true );
129
130#ifdef NEVER
131 clicked()
132 .connect( [=]( Wt::WModelIndex _index, Wt::WMouseEvent _event )
133 {
134 std::cout << __FILE__ << ":" << __LINE__ << " clicked row: " << _index.row() << " col: " << _index.column() << std::endl;
135 });
136
138 .connect( [=]( Wt::WModelIndex _index, Wt::WMouseEvent _event )
139 {
140 std::cout << __FILE__ << ":" << __LINE__ << " doubleClicked row: " << _index.row() << " col: " << _index.column() << std::endl;
141
142 });
143
145 .connect( [=]( Wt::WModelIndex _index, Wt::WMouseEvent _event )
146 {
147 std::cout << __FILE__ << ":" << __LINE__ << " mouseWentDown row: " << _index.row() << " col: " << _index.column() << std::endl;
148 });
149
150 mouseWentUp ()
151 .connect( [=]( Wt::WModelIndex _index, Wt::WMouseEvent _event )
152 {
153 std::cout << __FILE__ << ":" << __LINE__ << " mouseWentUp row: " << _index.row() << " col: " << _index.column() << std::endl;
154 });
155
156 touchStart ()
157 .connect( [=]( Wt::WModelIndex _index, Wt::WTouchEvent _event )
158 {
159 std::cout << __FILE__ << ":" << __LINE__ << " touchStart row: " << _index.row() << " col: " << _index.column() << std::endl;
160 });
161
162// touchStarted ()
163// .connect( [=]( Wt::WModelIndex _index, Wt::WTouchEvent _event )
164// {
165// std::cout << __FILE__ << ":" << __LINE__ << " touchStarted: " << std::endl;
166// });
167
168// touchMoved ()
169// .connect( [=]( Wt::WModelIndex _index, Wt::WTouchEvent _event )
170// {
171// std::cout << __FILE__ << ":" << __LINE__ << " touchMoved: " << std::endl;
172// });
173
174// touchEnded ()
175// .connect( [=]( Wt::WModelIndex _index, Wt::WTouchEvent _event )
176// {
177// std::cout << __FILE__ << ":" << __LINE__ << " touchEnded: " << std::endl;
178// });
179
181 .connect( [=]()
182 {
183 std::cout << __FILE__ << ":" << __LINE__ << " selectionChanged: " << std::endl;
184 });
185
186 pageChanged ()
187 .connect( [=]()
188 {
189 std::cout << __FILE__ << ":" << __LINE__ << " pageChanged: " << std::endl;
190 });
191
193 .connect( [=]( int, Wt::WLength)
194 {
195 std::cout << __FILE__ << ":" << __LINE__ << " pageChanged: " << std::endl;
196 });
197
198
200 .connect( [=]( int, Wt::WMouseEvent)
201 {
202 std::cout << __FILE__ << ":" << __LINE__ << " headerClicked: " << std::endl;
203 });
204
205
207 .connect( [=]( int, Wt::WMouseEvent )
208 {
209 std::cout << __FILE__ << ":" << __LINE__ << " headerDoubleClicked: " << std::endl;
210 });
211
212
214 .connect( [=]( int, Wt::WMouseEvent )
215 {
216 std::cout << __FILE__ << ":" << __LINE__ << " headerMouseWentDown: " << std::endl;
217 });
218
219
221 .connect( [=]( int, Wt::WMouseEvent )
222 {
223 std::cout << __FILE__ << ":" << __LINE__ << " headerMouseWentUp: " << std::endl;
224 });
225
226
227 scrolled ()
228 .connect( [=]( Wt::WScrollEvent _event )
229 {
230 std::cout << __FILE__ << ":" << __LINE__ << " scrolled"
231 << " x:" << _event.scrollX()
232 << " y:" << _event.scrollY()
233 << " w:" << _event.viewportWidth()
234 << " h:" << _event.viewportHeight()
235 << std::endl;
236 });
237#endif
238
239#ifdef NEVER
240 keyWentDown ()
241 .connect( [=]( Wt::WKeyEvent _event )
242 {
243 std::cout << __FILE__ << ":" << __LINE__ << " keyWentDown: " << _event.charCode() << " key:" << keyToCode( _event.key() ) << std::endl;
244 });
245
246
247 keyPressed ()
248 .connect( [=]( Wt::WKeyEvent _event )
249 {
250 std::cout << __FILE__ << ":" << __LINE__ << " keyPressed: " << _event.charCode() << " key:" << keyToCode( _event.key() ) << std::endl;
251 });
252
253 keyWentUp ()
254 .connect( [=]( Wt::WKeyEvent _event )
255 {
256 std::cout << __FILE__ << ":" << __LINE__ << " keyWentUp charCode:" << _event.charCode() << " key:" << keyToCode( _event.key() ) << std::endl;
257 });
258#endif
259
260} // endGCW::Gui::TableView::TableView( Wt::WContainerWidget* parent )
261
262auto
264layoutSizeChanged( int _width, int _height )-> void
265{
266#ifdef TABLEVIEW_HACK_V4_12_16
267 /*!
268 ** HACK HACK HACK
269 **
270 ** This is a total hack. Just upgraded to Wt-4.12.6 and now the WTableView is
271 ** calling layoutSizeChanged() repeatedly. It wasn't doing this in earlier
272 ** versions of the view. However, earlier versions of the view (not 4.10)
273 ** were unselectable. This (dumb) timer thing just prevents this function
274 ** from getting called over and over, until I have time to figure this out.
275 **
276 ** \todo remove this hack
277 **
278 */
279 std::cout << __FILE__ << ":" << __LINE__ << " " << __FUNCTION__ << "(" << " w:" << _width << ", h:" << _height << " ) " << " " << m_lastTime << " " << _elapsedTime << std::endl;
280 unsigned long long _currentTime = time(NULL);
281 unsigned long long _elapsedTime = _currentTime - m_lastTime;
282 if( _elapsedTime < 1 )
283 {
284 Wt::WTableView::layoutSizeChanged( _width, _height );
285 return;
286 }
287#endif
288
289 // Calculate our fixed width columns
290 auto nfixed = 0;
291 auto nrel = 0;
292
293 // "fixed" number of pixels
294 auto fixed = 0.0;
295
296 for( auto col : m_widths )
297 if( col.second.unit() == Wt::LengthUnit::Percentage )
298 nrel++;
299
300 auto percent_sum = 0.0;
301 for( auto col : m_widths )
302 {
303 if( col.second.unit() != Wt::LengthUnit::Percentage )
304 fixed += col.second.toPixels();
305 else
306 percent_sum += col.second.value();
307 }
308
309 // Check to see if the relative columns are taking up ~100% of the
310 // non-fixed width. "Roughly" because sometimes they're intentionally off
311 // by a fraction just to avoid a scroll bar
312 const auto epsilon = 0.5;
313 if( 100.0 - percent_sum > epsilon )
314 Wt::log("debug")
315 << "Warning: Relative column widths do not take up 100% of the available width: "
316 << percent_sum << "%"
317 ;
318
319 // Columns who's width wasn't explicitly set are considered "fixed", and Wt
320 // will default them to 150px or something.
321 if( model() )
322 {
323 nfixed = model()->columnCount() - nrel;
324
325 // Show scroll bar?
326 bool show_scroll = m_visible_rows > 0 && model()-> rowCount() > m_visible_rows;
327
328 auto remainder = _width - ( fixed + ( kTableCellPadding * model()->columnCount() ) + ( show_scroll ? kScrollBarWidth : 0.0 ) );
329
330 for( auto col : m_widths )
331 {
332 if( col.second.unit() == Wt::LengthUnit::Percentage )
333 {
334 setColumnWidth
335 (
336 col.first,
338 (
339 col.second.value()/100.0*remainder,
340 Wt::LengthUnit::Pixel
341 )
342 );
343
344 } // endif( col.second.unit() == Wt::LengthUnit::Percentage )
345
346 } // endfor( auto col : m_widths_)
347
348 } // endif( model() )
349
350 // Pass the call up the chain
351 Wt::WTableView::layoutSizeChanged( _width, _height );
352
353#ifdef TABLEVIEW_HACK_V4_12_16
354 m_lastTime = time(NULL);
355#endif
356
357} // endauto GCW::Gui::TableView::layoutSizeChanged( int _width, int _height ) -> void
358
359auto
361setColumnWidth( int column, const Wt::WLength& width )-> void
362{
363 // Just save the data and pass the the work up
364 m_widths.emplace( column, width );
365
366 Wt::WTableView::setColumnWidth( column, width );
367
368} // endauto GCW::Gui::TableView::setColumnWidth(int column, const Wt::WLength& width) -> void
369
370/*!
371** \brief Handle Click Event
372**
373** This handler responds to 'click' events in the view.
374**
375** The intent here is to provide some 'handling' when the user
376** is mouse-ing around the table view. He should be able to
377** click around to get editors to open, and select rows and
378** whatnot.
379**
380** The 'purpose' for the handler is due to the fact that the
381** view responds differently if the view is editable or not.
382** For instance, if an index is read-only, clicking on it
383** causes the view to 'select' the entire row. But, if the
384** index is 'Editable' then you are placed in to the editor
385** but the view 'selection' is still visible, even if it's on
386** another row. Further, the editor widget that opened, did
387** not signal to the rest of the row that it should be 'selected'.
388**
389*/
390#ifndef NEVER
391auto
393handleClick( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )-> void
394{
395#ifdef NEVER
396 std::cout << __FILE__ << ":" << __LINE__
397 << " handleClick:<start>"
398 << " row:" << _index.row()
399 << " col:" << _index.column()
400 << std::endl
401 ;
402#endif
403
404#ifdef NEVER
405 if( selectedIndexes().size() > 0 )
406 {
407 auto selectedIndex = *selectedIndexes().begin();
408
409#ifdef NEVER
410 std::cout << __FILE__ << ":" << __LINE__
411 << " selected:" << selectedIndexes().size()
412 << " row:" << selectedIndex.row()
413 << " col:" << selectedIndex.column()
414 << std::endl
415 ;
416#endif
417
418 /*
419 ** If the selection is different in any way, then clear
420 ** the entire selection. If the selection is the same
421 ** as the 'index', then we are handling the same click
422 ** on the same index that is already selected - don't
423 ** do anything. But if the _index is different than
424 ** the current selection, then we need to clear the
425 ** current selection.
426 **
427 */
428 if( selectedIndex.row () != _index.row ()
429 && selectedIndex.column () != _index.column ()
430 )
431 {
432 clearSelection();
433 }
434
435 closeEditors();
436
437 }
438#endif
439
440#ifdef NEVER
441 {
442 auto m = dynamic_cast< Wt::WStandardItemModel* >( model().get() );
443
444 auto row = _index.row();
445 for( int column = 0; column< m-> columnCount(); column++ )
446 {
447 std::cout << __FILE__ << ":" << __LINE__
448 << " row:" << row
449 << " col:" << column
450 << std::endl;
451
452 auto index = m-> index( row, column );
453 auto item = m-> itemFromIndex( index );
454
455 item-> setFlags( Wt::ItemFlag::Editable );
456// edit( index );
457
458 } // endfor( int column = 0; column< m-> columnCount(); column++ )
459 }
460#endif
461
462#ifdef NEVER
463 /*
464 ** The 'selector' is funky. If an 'editor' is enabled, then
465 ** selecting the cell causes the editor to open, but the previous
466 ** selection does not get un-selected, so it looks like a whole row
467 ** is selected, and then a cell is open in another row.
468 **
469 */
470 if( selectedIndexes().size() )
471 {
472 auto oldIndex = *selectedIndexes().begin();
473
474 std::cout << __FILE__ << ":" << __LINE__ << " handleCLick()"
475 << " old:" << oldIndex.row()
476 << " new:" << _index.row()
477 << std::endl;
478
479 if( _index.row() != oldIndex.row() )
480 {
481 clearSelection();
482 closeEditors( true );
483 }
484
485 }
486#endif
487
488 /*
489 ** Call the stock click handler
490 **
491 */
492 Wt::WTableView::handleClick( _index, _event );
493
494#ifdef NEVER
495 std::cout << __FILE__ << ":" << __LINE__ << " handleClick:<end>" << std::endl;
496#endif
497
498} // endvoid GCW::Gui::TableView::handleClick( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
499#endif
500
501#ifdef NEVER
502auto
504handleDoubleClick( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )-> void
505{
506#ifndef NEVER
507 std::cout << __FILE__ << ":" << __LINE__
508 << " handleDoubleClick:<start>"
509 << " row:" << _index.row()
510 << " col:" << _index.column()
511 << std::endl
512 ;
513#endif
514
515 Wt::WTableView::handleDoubleClick( _index, _event );
516
517#ifndef NEVER
518 std::cout << __FILE__ << ":" << __LINE__ << " handleDoubleClick:<end>" << std::endl;
519#endif
520
521} // endhandleDoubleClick ( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
522#endif
523
524#ifndef NEVER
525auto
527handleMouseDown( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )-> void
528{
529
530 // we can test the model and the table and the row and detect if the
531 // row we ~were~ on is dirty, and if it needs to be saved
532// Wt::WMessageBox::show( "TableView", "something or whatever", Wt::StandardButton::Ok );
533
534#ifdef NEVER
535 std::cout << BREAKHEADER
536 << " row:" << _index.row()
537 << " col:" << _index.column()
538 << std::endl
539 ;
540#endif
541
542#ifdef NEVER
543 if( m_lastIndexClick.isValid() )
544 if( m_lastIndexClick. row () != _index. row ()
545 || m_lastIndexClick. column () != _index. column ()
546 )
547 {
548 clearSelection();
549 closeEditors();
550 }
551#endif
552
553#ifdef NEVER
554 auto balanceIndex = model()-> index( _index.row(), 7 );
555
556 std::cout << __FILE__ << ":" << __LINE__ << " " <<balanceIndex.data( Wt::ItemDataRole::User ).type().name() << std::endl;
557
558 auto balanceValue = Wt::cpp17::any_cast< GCW_NUMERIC >( balanceIndex.data( Wt::ItemDataRole::User ) );
559 std::cout << __FILE__ << ":" << __LINE__ << " " << balanceValue << std::endl;
560#endif
561
562 Wt::WTableView::handleMouseDown( _index, _event );
563
564#ifdef NEVER
565 std::cout << BREAKFOOTER;
566#endif
567
568 /*
569 ** remember the last index clicked
570 **
571 */
572 m_lastIndexClick = _index;
573
574} // endhandleMouseDown( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
575#endif
576
577#ifdef NEVER
578auto
580handleMouseUp( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )-> void
581{
582#ifndef NEVER
583 std::cout << __FILE__ << ":" << __LINE__
584 << " handleMouseUp:<start>"
585 << " row:" << _index.row()
586 << " col:" << _index.column()
587 << std::endl
588 ;
589#endif
590
591 Wt::WTableView::handleMouseUp( _index, _event );
592
593#ifndef NEVER
594 std::cout << __FILE__ << ":" << __LINE__ << " handleMouseUp:<end>" << std::endl;
595#endif
596
597} // endhandleMouseUp( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
598#endif
599
600
601
static const int kTableBodyRowHeight
Definition TableView.h:32
auto handleClick(const Wt::WModelIndex &_index, const Wt::WMouseEvent &_event) -> void
Handle Click Event.
auto handleMouseDown(const Wt::WModelIndex &_index, const Wt::WMouseEvent &_event) -> void
static const int kTableHeaderRowHeight
Definition TableView.h:29
static const int kScrollBarWidth
Definition TableView.h:38
auto layoutSizeChanged(int _width, int _height) -> void override
static const int kTableCellPadding
Definition TableView.h:35
auto setColumnWidth(int _column, const Wt::WLength &_width) -> void override
static const int kDefaultDateTimeWidth
Definition TableView.h:41
static constexpr const int User
virtual Wt::Signals::connection connect(WObject *target, WObject::Method method) override
virtual void handleMouseUp(const WModelIndex &index, const WMouseEvent &event)
Signal< WModelIndex, WMouseEvent > & mouseWentUp()
virtual void handleMouseDown(const WModelIndex &index, const WMouseEvent &event)
Signal & selectionChanged()
EventSignal< WKeyEvent > & keyPressed()
Signal< int, WMouseEvent > & headerClicked()
EventSignal< WKeyEvent > & keyWentDown()
EventSignal< WKeyEvent > & keyWentUp()
virtual void handleClick(const WModelIndex &index, const WMouseEvent &event)
Signal< WModelIndex, WMouseEvent > & mouseWentDown()
Signal< WModelIndex, WTouchEvent > & touchStart()
Signal< WModelIndex, WMouseEvent > & clicked()
Signal< int, WMouseEvent > & headerMouseWentUp()
Signal< WModelIndex, WMouseEvent > & doubleClicked()
Signal< int, WMouseEvent > & headerMouseWentDown()
virtual void handleDoubleClick(const WModelIndex &index, const WMouseEvent &event)
Signal< int, WLength > & columnResized()
Signal< int, WMouseEvent > & headerDoubleClicked()
virtual void setAttributeValue(const std::string &name, const WString &value) override
virtual void addStyleClass(const WString &styleClass, bool force=false) override
int row() const
int column() const
virtual void setColumnWidth(int column, const WLength &width) override
virtual EventSignal< WScrollEvent > & scrolled() override
virtual void setHeaderHeight(const WLength &height) override
virtual void setRowHeight(const WLength &rowHeight) override
void setLayoutSizeAware(bool sizeAware)
virtual void layoutSizeChanged(int width, int height)
#define BREAKHEADER
Definition gcwglobal.h:45
#define BREAKFOOTER
Definition gcwglobal.h:46