Wtx ~ Wt Extension Library
WtxLib
FieldDefLookupTable.cpp
1 /**************************************************************************
2 ###########################################################################
3 ##
4 ## $SHOWOFFDB_BEGIN_LICENSE$
5 ## Copyright (C) 2011 Lorimark Solutions, LLC and/or its subsidiary(-ies).
6 ## All rights reserved.
7 ## Contact: Lorimark Solutions, LLC (info@showoff-db.org)
8 ##
9 ## This file is part of the Showoff Database Application Framework.
10 ##
11 ## Commercial Usage
12 ## Licensees holding valid ShowoffDB Commercial licenses may use this file in
13 ## accordance with the ShowoffDB Commercial License Agreement provided with the
14 ## Software or, alternatively, in accordance with the terms contained in
15 ## a written agreement between you and Lorimark Solutions, LLC.
16 ##
17 ## GNU Lesser General Public License Usage
18 ## Alternatively, this file may be used under the terms of the GNU Lesser
19 ## General Public License version 2.1 as published by the Free Software
20 ## Foundation and appearing in the file LICENSE.LGPL included in the
21 ## packaging of this file. Please review the following information to
22 ## ensure the GNU Lesser General Public License version 2.1 requirements
23 ## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ##
25 ## In addition, as a special exception, Lorimark Solutions, LLC gives
26 ## you certain additional rights. These rights are described in the
27 ## Lorimark Solutions, LLC ShowoffDB LGPL Exception version 1.0, included in
28 ## the file LGPL_EXCEPTION.txt in this package.
29 ##
30 ## GNU General Public License Usage
31 ## Alternatively, this file may be used under the terms of the GNU
32 ## General Public License version 3.0 as published by the Free Software
33 ## Foundation and appearing in the file LICENSE.GPL included in the
34 ## packaging of this file. Please review the following information to
35 ## ensure the GNU General Public License version 3.0 requirements will be
36 ## met: http://www.gnu.org/copyleft/gpl.html.
37 ##
38 ## If you have questions regarding the use of this file, please contact
39 ## Lorimark Solutions, LLC at info@showoff-db.org.
40 ## $SHOWOFFDB_END_LICENSE$
41 ##
42 #############################################################################
43 ****************************************************************************/
44 
45 #include <Wt/WText.h>
46 #include <Wt/WDialog.h>
47 #include <Wt/WPushButton.h>
48 #include <Wt/WContainerWidget.h>
49 #include <Wt/WAbstractItemModel.h>
50 #include <Wt/WLineEdit.h>
51 #include <Wt/WVBoxLayout.h>
52 #include <Wt/WTableView.h>
53 #include <Wt/WModelIndex.h>
54 
55 #include <Wtx/Core/Core.h>
56 #include <Wtx/Util/Util.h>
57 
58 #include "FieldDefLookupTable.h"
59 
60 namespace STATIC {
61 
62 class TableView
63 : public Wt::WContainerWidget
64 {
65  public:
66 
67  TableView();
68 
69  void setModel( const std::shared_ptr<Wt::WAbstractItemModel > & model );
70 
71  std::shared_ptr<Wt::WAbstractItemModel > m_model;
72 
73 }; // endclass TableView
74 
75 TableView::TableView()
76 : Wt::WContainerWidget()
77 {
78 }
79 
80 void TableView::setModel( const std::shared_ptr<Wt::WAbstractItemModel > & model )
81 {
82 }
83 
84 
86 : public Wt::WLineEdit
87 {
88  public:
89 
91  (
92  int sid,
93  std::shared_ptr<Wt::WAbstractItemModel> (*getModel)(int sid,int tid,const std::string & filter,Wtx::Dbo::Session & session),
94  std::unique_ptr<Wt::WTableView> (*getTableView)(int sid,int tid,const std::string & filter,Wtx::Dbo::Session & session),
96  )
97  : m_session(s),
98  m_sourceID(sid),
99  m_getModel(getModel),
100  m_getTableView(getTableView)
101  {
102  changed() .connect( this, &TableLineEdit::on_changed );
103  focussed() .connect( this, &TableLineEdit::on_focussed );
104 // textInput() .connect( this, &TableLineEdit::on_textInput );
105 
106  } // endSmartLineEdit( std::shared_ptr<Wt::WAbstractItemModel> model, Wtx::Dbo::Session & s )
107 
108  /*
109  ** this returns the ID value of the string
110  **
111  */
112  int find_id( const std::string & value )
113  {
114  int retVal = -1;
115  int count = 0;
116  if( m_model )
117  {
118 
119  auto v = Wtx::Core::toupper( value );
120 
121  for( int i=0; i < m_model-> rowCount(); i++ )
122  {
123  auto id = Wt::asNumber(m_model-> index( i, 0 ).data());
124  auto ky = Wtx::Core::toupper( Wt::asString(m_model-> index( i, 1 ).data()).toUTF8() );
125 
126  if( ky.find(v) != std::string::npos )
127  {
128  count++;
129  retVal = id;
130  }
131 
132  if( count > 1 )
133  break;
134 
135  }
136 
137  if( count == 1 )
138  return retVal;
139  }
140 
141  return -1;
142 
143  } // endint get_id( const std::string & value )
144 
145  /*
146  ** this returns the ID value of the string
147  **
148  */
149  int get_id( const std::string & value )
150  {
151  if( m_model )
152  {
153  for( int i=0; i < m_model-> rowCount(); i++ )
154  {
155  auto id = Wt::asNumber(m_model-> index( i, 0 ).data());
156  auto ky = Wt::asString(m_model-> index( i, 1 ).data()).toUTF8();
157 
158  if( value == ky )
159  {
160  return id;
161  }
162 
163  }
164  }
165 
166  return -1;
167 
168  } // endint get_id( const std::string & value )
169 
170  /*
171  ** this returns the string value of the ID
172  **
173  */
174  std::string get_string( int index )
175  {
176  if( m_model && index > -1 )
177  {
178  for( int i=0; i < m_model-> rowCount(); i++ )
179  {
180  auto id = Wt::asNumber( m_model-> index( i, 0 ).data() );
181  auto ky = Wt::asString( m_model-> index( i, 1 ).data() ).toUTF8();
182 
183  if( index == id )
184  {
185  return ky;
186  }
187 
188  }
189 
190  }
191 
192  return "";
193 
194  } // endstd::string get_string( int index )
195 
196  /*
197  ** this is kind of crummy. this is supposed to load
198  ** the model, but if an .id. value is specified then
199  ** its supposed to only load that id. but if no .id.
200  ** is specified (id==-1) then this is supposed to
201  ** load the _whole_ model. The idea being, it should
202  ** not load the model until it's absolutely necessary.
203  ** the concept is that we only need to load one item
204  ** out of the model if we're just loading the widget.
205  ** we only need 1 item to get the widget loaded, to
206  ** convert the id number in to a string-value. but
207  ** if the user never selects this control, then there's
208  ** no reason to load ALL the data, it just slows down
209  ** the user interface. this is my best attempt at
210  ** lazy-loading.
211  **
212  */
213  void loadModel( int id, const std::string & filter )
214  {
215  if( !m_loaded && m_getModel )
216  {
217 // std::cout << __FILE__ << ":" << __LINE__ << " Loading Model: " << id << " " << m_sourceID << " " << m_targetID << std::endl;
218  m_model = m_getModel( m_sourceID, id, filter, m_session );
219 // setModel( m_model );
220 // setModelColumn(1);
221 
222 // std::cout << __FILE__ << ":" << __LINE__ << " " << (void*)m_model.get() << std::endl;
223 // std::cout << __FILE__ << ":" << __LINE__ << " " << m_model-> rowCount() << std::endl;
224 
225  if( id == -1 )
226  m_loaded = true;
227  }
228  }
229 
230  void on_focussed()
231  {
232 #ifdef NEVER
233  std::cout << __FILE__ << ":" << __LINE__
234  << " " << m_sourceID
235  << " " << m_targetID
236  << " " << m_valueText
237  << std::endl
238  ;
239 #endif
240 
241  loadModel(-1,"");
242 
243 // for( int i=0; i< count(); i++ )
244 // if( itemText(i) == m_valueText )
245 // setCurrentIndex(i);
246 
247  setValueText( m_valueText );
248 
249  }
250 
251 // void on_textInput()
252 // {
253 // std::cout << __FILE__ << ":" << __LINE__ << " " << Wt::WLineEdit::valueText() << std::endl;
254 //
255 // if( !m_table )
256 // {
257 // }
258 // }
259 
260  /*
261  ** this fires when the widget looses focus and the value in it
262  ** is different than from when it started
263  **
264  */
265  void on_changed()
266  {
267  auto value = Wt::WLineEdit::valueText().toUTF8();
268 
269  if( value == "" )
270  {
271  m_targetID = -1;
272  m_valueText = "";
273  return;
274  }
275 
276  m_targetID = find_id( value );
277 
278  if( m_targetID == -1 )
279  {
280  auto dialog = addChild( std::make_unique<Wt::WDialog>("item picker") );
281  dialog-> rejectWhenEscapePressed(true);
282  dialog-> show();
283  auto cw = dialog-> contents()-> addNew<Wt::WContainerWidget>();
284  auto lw = std::make_unique<Wt::WVBoxLayout>();
285 
286  auto u_search = std::make_unique<Wt::WLineEdit>(value);
287  auto search = u_search.get();
288  search-> setDisabled(true);
289  lw-> addWidget( std::move( u_search ) );
290 
291  if( m_getTableView )
292  {
293 #ifdef NEVER
294  std::cout << __FILE__ << ":" << __LINE__
295  << " " << m_sourceID
296  << " " << m_targetID
297  << " " << search-> valueText().toUTF8()
298  << std::endl
299  ;
300 #endif
301 
302  auto u_table = m_getTableView( m_sourceID, m_targetID, search-> valueText().toUTF8(), m_session );
303  auto table = u_table.get();
304 
305  /*
306  ** make sure we actually ended up with a table-view
307  **
308  */
309  if( table )
310  {
311  table-> doubleClicked().connect( dialog, &Wt::WDialog::accept );
312  lw-> addWidget( std::move( u_table ), 1 );
313 
314  auto u_select = std::make_unique<Wt::WPushButton>("select");
315  u_select-> clicked().connect( dialog, &Wt::WDialog::accept );
316  lw-> addWidget( std::move( u_select ) );
317 
318  dialog-> finished().connect( [=]( Wt::DialogCode code )
319  {
320  /*
321  ** he must hit the 'select' button
322  **
323  */
324  if( dialog-> result() != Wt::DialogCode::Accepted )
325  return;
326 
327  /*
328  ** at most/least one row must be selected
329  **
330  */
331  if( table-> selectedIndexes().size() != 1 )
332  return;
333 
334  auto index = *table-> selectedIndexes().begin();
335 
336  m_targetID = Wt::asNumber( index.data() );
337 #ifdef NEVER
338  std::cout << __FILE__ << ":" << __LINE__
339  << " " << index.row()
340  << " " << index.column()
341  << " " << m_targetID
342  << std::endl
343  ;
344 #endif
345 
346  setValueText( get_string( m_targetID ) );
347 
348  });
349 
350  }
351 
352  /*
353  ** even though we asked for a table-vew, we could not get one
354  ** so alert the user.
355  **
356  */
357  else
358  {
359  lw-> addWidget
360  (
361  std::make_unique<Wt::WText>
362  (
363  Wt::WString("FieldDefLookupTable.cpp:{1} 'getTableView' not returning view '{2}'")
364  .arg( __LINE__ )
365  .arg( m_field )
366  )
367  );
368 
369  }
370 
371  }
372 
373  else // if( !m_getTableView )
374  {
375  lw-> addWidget
376  (
377  std::make_unique<Wt::WText>
378  (
379  Wt::WString("FieldDefLookupTable.cpp:{1} 'getTableView' not set '{2}'")
380  .arg( __LINE__ )
381  .arg( m_field )
382  )
383  );
384  }
385 
386  auto u_cancel = std::make_unique<Wt::WPushButton>("cancel");
387  u_cancel-> clicked().connect( dialog, &Wt::WDialog::reject );
388  lw-> addWidget( std::move( u_cancel ) );
389 
390  cw-> setLayout( std::move(lw) );
391 
392  } // endif( m_targetID == -1 )
393 
394  else
395  {
396  setValueText( get_string( m_targetID ) );
397  }
398 
399 #ifdef NEVER
400  std::cout << __FILE__ << ":" << __LINE__
401  << "\n targetID: " << m_targetID
402  << "\n valueText: " << m_valueText
403  << std::endl
404  ;
405 #endif
406 
407 #ifdef NEVER
408  auto dialog = addChild( std::make_unique<Wt::WDialog>() );
409  dialog-> rejectWhenEscapePressed();
410  dialog-> contents()-> addNew<Wt::WText>("cannot put in false data");
411  dialog-> show();
412 #endif
413 
414 // std::cout << __FILE__ << ":" << __LINE__ << " " << std::endl;
415 
416  } // endvoid on_changed()
417 
418 
419  Wt::WString valueText() const
420  {
421  auto retVal = Wt::WString("[null]");
422 
423  if( m_targetID != -1 )
424  retVal = Wt::WString("{1}: {2}]").arg( m_table ).arg( m_targetID );
425 
426 #ifdef NEVER
427  std::cout << __FILE__ << ":" << __LINE__
428  << "\n m_loaded: " << m_loaded
429  << "\n m_field: " << m_field
430  << "\n m_table: " << m_table
431  << "\n m_sourceID: " << m_sourceID
432  << "\n m_targetID: " << m_targetID
433  << "\n retVal: " << retVal
434  << std::endl
435  ;
436 #endif
437 
438  return retVal;
439 
440  } // endWt::WString valueText() const
441 
442  void setValueText( const Wt::WString & value )
443  {
444  m_valueText = value.toUTF8();
445 
446  /*
447  ** if the value text is nothing, then wipe
448  ** it so that nothing shows.
449  **
450  */
451  if( m_valueText == "[null]" )
452  m_valueText = "";
453 
454  else
455  {
456  if( m_valueText.find(":") != std::string::npos )
457  {
458  auto v = Wtx::Core::split( m_valueText, ':' );
459  m_table = v.at(0);
460  m_targetID = std::stoi(v.at(1));
461  loadModel(m_targetID,"");
462  m_valueText = get_string( m_targetID );
463 
464  }
465 
466  }
467 
468  Wt::WLineEdit::setValueText( m_valueText );
469 
470 #ifdef NEVER
471  std::cout << __FILE__ << ":" << __LINE__
472  << "\n loaded: " << m_loaded
473  << "\n sourceID: " << m_sourceID
474  << "\n targetID: " << m_targetID
475  << "\n table: " << m_table
476  << "\n value: " << value
477  << "\n valueText:" << m_valueText
478  << "\n valueText:" << Wt::WLineEdit::valueText()
479  << std::endl
480  ;
481 #endif
482 
483  } // endvoid setValueText( const Wt::WString & value )
484 
485  std::shared_ptr<Wt::WAbstractItemModel> (*m_getModel)(int sid, int tid, const std::string & filter, Wtx::Dbo::Session & session) = nullptr;
486  std::unique_ptr<Wt::WTableView> (*m_getTableView)(int sid, int tid, const std::string & filter, Wtx::Dbo::Session & session) = nullptr;
487  bool m_loaded = false;
488  std::string m_field;
489  std::string m_table;
490  int m_sourceID = -1;
491  int m_targetID = -1;
492  std::string m_valueText;
493  Wtx::Dbo::Session & m_session;
494  std::shared_ptr<Wt::WAbstractItemModel> m_model = nullptr;
495 
496 }; // endclass SmartLineEdit
497 
498 } // endnamespace STATIC
499 
501 (
502  TableDef & td,
503  const char * fn,
504  int w,
505  int h,
506  Flags f,
507  std::string lb,
508  std::string ph,
509  std::string inf,
510  std::string tt,
511  int ht
512 )
513 : Wtx::Dbo::FieldDefLookupBase( td, fn, w, w, f, lb, ph, inf, tt, ht )
514 {
515 }
516 
518 (
519  TableDef & td,
520  const char * fn,
521  std::string lb,
522  std::string ph,
523  std::string inf,
524  std::string tt,
525  int ht
526 )
527 : Wtx::Dbo::FieldDefLookupBase( td, fn, lb, ph, inf, tt, ht )
528 {
529 }
530 
531 std::unique_ptr<Wt::WWidget> Wtx::Dbo::FieldDefLookupTable::createEditWidget( int sid, Wtx::Dbo::Session & session ) const
532 {
533 // std::cout << __FILE__ << ":" << __LINE__
534 // << "\n sid: " << sid
535 // << std::endl
536 // ;
537 
538  auto retVal = std::make_unique<STATIC::TableLineEdit>( sid, getLookupModel, getTableView, session );
539  retVal-> m_table = "[" + targetTable;
540  retVal-> m_field = fieldName();
541 
542  return retVal;
543 
544 }
545 
Table Definition.
Definition: TableDef.h:72
Flags
Field Definition Flags.
Definition: FieldDefBase.h:101
FieldDefLookupTable(TableDef &td, const char *fn, int w=-1, int h=-1, Flags f=Flags::None, std::string lb="", std::string ph="", std::string inf="", std::string tt="", int ht=-1)
ctor