GnuCashew ~ GnuCash Enabled Web
GCW
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 
18 namespace {
19 static 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 
100 TableView()
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
113  setAttributeValue
114  (
115  "oncontextmenu",
116  "event.cancelBubble = true; event.returnValue = false; return false;"
117  );
118 #endif
119 
120  setHeaderHeight( kTableHeaderRowHeight );
121  setRowHeight( kTableBodyRowHeight );
122 
123  setLayoutSizeAware( true );
124 
125 #ifdef NEVER
126  clicked()
127  .connect( [=]( Wt::WModelIndex _index, Wt::WMouseEvent _event )
128  {
129  std::cout << __FILE__ << ":" << __LINE__ << " clicked row: " << _index.row() << " col: " << _index.column() << std::endl;
130  });
131 
132  doubleClicked()
133  .connect( [=]( Wt::WModelIndex _index, Wt::WMouseEvent _event )
134  {
135  std::cout << __FILE__ << ":" << __LINE__ << " doubleClicked row: " << _index.row() << " col: " << _index.column() << std::endl;
136 
137  });
138 
139  mouseWentDown ()
140  .connect( [=]( Wt::WModelIndex _index, Wt::WMouseEvent _event )
141  {
142  std::cout << __FILE__ << ":" << __LINE__ << " mouseWentDown row: " << _index.row() << " col: " << _index.column() << std::endl;
143  });
144 
145  mouseWentUp ()
146  .connect( [=]( Wt::WModelIndex _index, Wt::WMouseEvent _event )
147  {
148  std::cout << __FILE__ << ":" << __LINE__ << " mouseWentUp row: " << _index.row() << " col: " << _index.column() << std::endl;
149  });
150 
151  touchStart ()
152  .connect( [=]( Wt::WModelIndex _index, Wt::WTouchEvent _event )
153  {
154  std::cout << __FILE__ << ":" << __LINE__ << " touchStart row: " << _index.row() << " col: " << _index.column() << std::endl;
155  });
156 
157 // touchStarted ()
158 // .connect( [=]( Wt::WModelIndex _index, Wt::WTouchEvent _event )
159 // {
160 // std::cout << __FILE__ << ":" << __LINE__ << " touchStarted: " << std::endl;
161 // });
162 
163 // touchMoved ()
164 // .connect( [=]( Wt::WModelIndex _index, Wt::WTouchEvent _event )
165 // {
166 // std::cout << __FILE__ << ":" << __LINE__ << " touchMoved: " << std::endl;
167 // });
168 
169 // touchEnded ()
170 // .connect( [=]( Wt::WModelIndex _index, Wt::WTouchEvent _event )
171 // {
172 // std::cout << __FILE__ << ":" << __LINE__ << " touchEnded: " << std::endl;
173 // });
174 
175  selectionChanged ()
176  .connect( [=]()
177  {
178  std::cout << __FILE__ << ":" << __LINE__ << " selectionChanged: " << std::endl;
179  });
180 
181  pageChanged ()
182  .connect( [=]()
183  {
184  std::cout << __FILE__ << ":" << __LINE__ << " pageChanged: " << std::endl;
185  });
186 
187  columnResized ()
188  .connect( [=]( int, Wt::WLength)
189  {
190  std::cout << __FILE__ << ":" << __LINE__ << " pageChanged: " << std::endl;
191  });
192 
193 
194  headerClicked ()
195  .connect( [=]( int, Wt::WMouseEvent)
196  {
197  std::cout << __FILE__ << ":" << __LINE__ << " headerClicked: " << std::endl;
198  });
199 
200 
201  headerDoubleClicked ()
202  .connect( [=]( int, Wt::WMouseEvent )
203  {
204  std::cout << __FILE__ << ":" << __LINE__ << " headerDoubleClicked: " << std::endl;
205  });
206 
207 
208  headerMouseWentDown ()
209  .connect( [=]( int, Wt::WMouseEvent )
210  {
211  std::cout << __FILE__ << ":" << __LINE__ << " headerMouseWentDown: " << std::endl;
212  });
213 
214 
215  headerMouseWentUp ()
216  .connect( [=]( int, Wt::WMouseEvent )
217  {
218  std::cout << __FILE__ << ":" << __LINE__ << " headerMouseWentUp: " << std::endl;
219  });
220 
221 
222  scrolled ()
223  .connect( [=]( Wt::WScrollEvent _event )
224  {
225  std::cout << __FILE__ << ":" << __LINE__ << " scrolled"
226  << " x:" << _event.scrollX()
227  << " y:" << _event.scrollY()
228  << " w:" << _event.viewportWidth()
229  << " h:" << _event.viewportHeight()
230  << std::endl;
231  });
232 #endif
233 
234 #ifdef NEVER
235  keyWentDown ()
236  .connect( [=]( Wt::WKeyEvent _event )
237  {
238  std::cout << __FILE__ << ":" << __LINE__ << " keyWentDown: " << _event.charCode() << " key:" << keyToCode( _event.key() ) << std::endl;
239  });
240 
241 
242  keyPressed ()
243  .connect( [=]( Wt::WKeyEvent _event )
244  {
245  std::cout << __FILE__ << ":" << __LINE__ << " keyPressed: " << _event.charCode() << " key:" << keyToCode( _event.key() ) << std::endl;
246  });
247 
248  keyWentUp ()
249  .connect( [=]( Wt::WKeyEvent _event )
250  {
251  std::cout << __FILE__ << ":" << __LINE__ << " keyWentUp charCode:" << _event.charCode() << " key:" << keyToCode( _event.key() ) << std::endl;
252  });
253 #endif
254 
255 } // endGCW::Gui::TableView::TableView( Wt::WContainerWidget* parent )
256 
257 auto
259 layoutSizeChanged( int width, int height )-> void
260 {
261  // Calculate our fixed width columns
262  auto nfixed = 0;
263  auto nrel = 0;
264 
265  // "fixed" number of pixels
266  auto fixed = 0.0;
267 
268  for( auto col : widths_ )
269  if( col.second.unit() == Wt::LengthUnit::Percentage )
270  nrel++;
271 
272  auto percent_sum=0.0;
273  for( auto col : widths_)
274  {
275  if( col.second.unit() != Wt::LengthUnit::Percentage )
276  fixed += col.second.toPixels();
277  else
278  percent_sum += col.second.value();
279  }
280 
281  // Check to see if the relative columns are taking up ~100% of the
282  // non-fixed width. "Roughly" because sometimes they're intentionally off
283  // by a fraction just to avoid a scroll bar
284  const auto epsilon = 0.5;
285  if( 100.0 - percent_sum > epsilon )
286  Wt::log("debug")
287  << "Warning: Relative column widths do not take up 100% of the available width"
288  ;
289 
290  // Columns who's width wasn't explicitly set are considered "fixed", and Wt
291  // will default them to 150px or something.
292  if( model() )
293  {
294  nfixed = model()->columnCount() - nrel;
295 
296  // Show scroll bar?
297  bool show_scroll = visible_rows_ > 0 && model()->rowCount() > visible_rows_;
298 
299  auto remainder = width - ( fixed + ( kTableCellPadding * model()->columnCount() ) + ( show_scroll ? kScrollBarWidth : 0.0 ) );
300 
301  for( auto col : widths_)
302  if( col.second.unit() == Wt::LengthUnit::Percentage )
303  setColumnWidth
304  (
305  col.first,
306  Wt::WLength
307  (
308  col.second.value()/100.0*remainder,
309  Wt::LengthUnit::Pixel
310  )
311  );
312 
313  } // endif( model() )
314 
315  // Pass the call up the chain
316  Wt::WTableView::layoutSizeChanged( width, height );
317 
318 } // endauto GCW::Gui::TableView::layoutSizeChanged( int width, int height ) -> void
319 
321 setColumnWidth( int column, const Wt::WLength& width )-> void
322 {
323  // Just save the data and pass the the work up
324  widths_.emplace( column, width );
325 
326  Wt::WTableView::setColumnWidth( column, width );
327 
328 } // endauto GCW::Gui::TableView::setColumnWidth(int column, const Wt::WLength& width) -> void
329 
330 /*!
331 ** \brief Handle Click Event
332 **
333 ** This handler responds to 'click' events in the view.
334 **
335 ** The intent here is to provide some 'handling' when the user
336 ** is mouse-ing around the table view. He should be able to
337 ** click around to get editors to open, and select rows and
338 ** whatnot.
339 **
340 ** The 'purpose' for the handler is due to the fact that the
341 ** view responds differently if the view is editable or not.
342 ** For instance, if an index is read-only, clicking on it
343 ** causes the view to 'select' the entire row. But, if the
344 ** index is 'Editable' then you are placed in to the editor
345 ** but the view 'selection' is still visible, even if it's on
346 ** another row. Further, the editor widget that opened, did
347 ** not signal to the rest of the row that it should be 'selected'.
348 **
349 */
350 #ifdef NEVER
351 void
352 GCW::Gui::TableView::
353 handleClick( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
354 {
355 #ifndef NEVER
356  std::cout << __FILE__ << ":" << __LINE__
357  << " handleClick:<start>"
358  << " row:" << _index.row()
359  << " col:" << _index.column()
360  << std::endl
361  ;
362 #endif
363 
364 #ifdef NEVER
365  if( selectedIndexes().size() > 0 )
366  {
367  auto selectedIndex = *selectedIndexes().begin();
368 
369 #ifdef NEVER
370  std::cout << __FILE__ << ":" << __LINE__
371  << " selected:" << selectedIndexes().size()
372  << " row:" << selectedIndex.row()
373  << " col:" << selectedIndex.column()
374  << std::endl
375  ;
376 #endif
377 
378  /*
379  ** If the selection is different in any way, then clear
380  ** the entire selection. If the selection is the same
381  ** as the 'index', then we are handling the same click
382  ** on the same index that is already selected - don't
383  ** do anything. But if the _index is different than
384  ** the current selection, then we need to clear the
385  ** current selection.
386  **
387  */
388  if( selectedIndex.row () != _index.row ()
389  && selectedIndex.column () != _index.column ()
390  )
391  {
392  clearSelection();
393  }
394 
395  closeEditors();
396 
397  }
398 #endif
399 
400 #ifdef NEVER
401  {
402  auto m = dynamic_cast< Wt::WStandardItemModel* >( model().get() );
403 
404  auto row = _index.row();
405  for( int column = 0; column< m-> columnCount(); column++ )
406  {
407  std::cout << __FILE__ << ":" << __LINE__
408  << " row:" << row
409  << " col:" << column
410  << std::endl;
411 
412  auto index = m-> index( row, column );
413  auto item = m-> itemFromIndex( index );
414 
415  item-> setFlags( Wt::ItemFlag::Editable );
416 // edit( index );
417 
418  } // endfor( int column = 0; column< m-> columnCount(); column++ )
419  }
420 #endif
421 
422 #ifdef NEVER
423  /*
424  ** The 'selector' is funky. If an 'editor' is enabled, then
425  ** selecting the cell causes the editor to open, but the previous
426  ** selection does not get un-selected, so it looks like a whole row
427  ** is selected, and then a cell is open in another row.
428  **
429  */
430  if( selectedIndexes().size() )
431  {
432  auto oldIndex = *selectedIndexes().begin();
433 
434  std::cout << __FILE__ << ":" << __LINE__ << " handleCLick()"
435  << " old:" << oldIndex.row()
436  << " new:" << _index.row()
437  << std::endl;
438 
439  if( _index.row() != oldIndex.row() )
440  {
441  clearSelection();
442  closeEditors( true );
443  }
444 
445  }
446 #endif
447 
448  /*
449  ** Call the stock click handler
450  **
451  */
452  Wt::WTableView::handleClick( _index, _event );
453 
454 #ifndef NEVER
455  std::cout << __FILE__ << ":" << __LINE__ << " handleClick:<end>" << std::endl;
456 #endif
457 
458 } // endvoid GCW::Gui::TableView::handleClick( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
459 #endif
460 
461 #ifdef NEVER
462 void
463 GCW::Gui::TableView::
464 handleDoubleClick( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
465 {
466 #ifndef NEVER
467  std::cout << __FILE__ << ":" << __LINE__
468  << " handleDoubleClick:<start>"
469  << " row:" << _index.row()
470  << " col:" << _index.column()
471  << std::endl
472  ;
473 #endif
474 
475  Wt::WTableView::handleDoubleClick( _index, _event );
476 
477 #ifndef NEVER
478  std::cout << __FILE__ << ":" << __LINE__ << " handleDoubleClick:<end>" << std::endl;
479 #endif
480 
481 } // endhandleDoubleClick ( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
482 #endif
483 
484 #ifndef NEVER
485 void
487 handleMouseDown( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
488 {
489 
490  // we can test the model and the table and the row and detect if the
491  // row we ~were~ on is dirty, and if it needs to be saved
492 // Wt::WMessageBox::show( "TableView", "something or whatever", Wt::StandardButton::Ok );
493 
494 #ifdef NEVER
495  std::cout << BREAKHEADER
496  << " row:" << _index.row()
497  << " col:" << _index.column()
498  << std::endl
499  ;
500 #endif
501 
502 #ifdef NEVER
503  if( m_lastIndexClick.isValid() )
504  if( m_lastIndexClick. row () != _index. row ()
505  || m_lastIndexClick. column () != _index. column ()
506  )
507  {
508  clearSelection();
509  closeEditors();
510  }
511 #endif
512 
513 #ifdef NEVER
514  auto balanceIndex = model()-> index( _index.row(), 7 );
515 
516  std::cout << __FILE__ << ":" << __LINE__ << " " <<balanceIndex.data( Wt::ItemDataRole::User ).type().name() << std::endl;
517 
518  auto balanceValue = Wt::cpp17::any_cast< GCW_NUMERIC >( balanceIndex.data( Wt::ItemDataRole::User ) );
519  std::cout << __FILE__ << ":" << __LINE__ << " " << balanceValue << std::endl;
520 #endif
521 
522  Wt::WTableView::handleMouseDown( _index, _event );
523 
524 #ifdef NEVER
525  std::cout << BREAKFOOTER;
526 #endif
527 
528  /*
529  ** remember the last index clicked
530  **
531  */
532  m_lastIndexClick = _index;
533 
534 } // endhandleMouseDown ( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
535 #endif
536 
537 #ifdef NEVER
538 void
539 GCW::Gui::TableView::
540 handleMouseUp( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
541 {
542 #ifndef NEVER
543  std::cout << __FILE__ << ":" << __LINE__
544  << " handleMouseUp:<start>"
545  << " row:" << _index.row()
546  << " col:" << _index.column()
547  << std::endl
548  ;
549 #endif
550 
551  Wt::WTableView::handleMouseUp( _index, _event );
552 
553 #ifndef NEVER
554  std::cout << __FILE__ << ":" << __LINE__ << " handleMouseUp:<end>" << std::endl;
555 #endif
556 
557 } // endhandleMouseUp( const Wt::WModelIndex & _index, const Wt::WMouseEvent & _event )
558 #endif
559 
560 
561 
static const int kTableBodyRowHeight
Definition: TableView.h:31
static const int kTableHeaderRowHeight
Definition: TableView.h:28
static const int kScrollBarWidth
Definition: TableView.h:37
auto layoutSizeChanged(int width, int height) -> void override
Definition: TableView.cpp:259
auto handleMouseDown(const Wt::WModelIndex &_index, const Wt::WMouseEvent &_event) -> void
Handle Click Event.
Definition: TableView.cpp:487
static const int kTableCellPadding
Definition: TableView.h:34
auto setColumnWidth(int column, const Wt::WLength &width) -> void override
Definition: TableView.cpp:321
static const int kDefaultDateTimeWidth
Definition: TableView.h:40
#define BREAKHEADER
Definition: gcwglobal.h:42
#define BREAKFOOTER
Definition: gcwglobal.h:43
Definition: GncLock.h:6