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