UltraScan III
us_data_tree.cpp
Go to the documentation of this file.
1 
3 #include "us_data_tree.h"
4 #include "us_settings.h"
5 #include "us_gui_settings.h"
6 #include "us_constants.h"
7 #include "us_passwd.h"
8 #include "us_editor.h"
9 #include "us_noise.h"
10 #include "us_util.h"
11 
12 const QColor colorRed( 240, 0, 0 );
13 const QColor colorBlue( 0, 0, 255 );
14 const QColor colorBrown( 120, 60, 0 );
15 const QColor colorGreen( 0, 150, 0 );
16 const QColor colorGray( 110, 110, 110 );
17 const QColor colorWhite( 255, 255, 240 );
18 
19 // class to manage a tree widget, underlying data, and processing
20 US_DataTree::US_DataTree( US_DataModel* dmodel, QTreeWidget* treewidg,
21  QWidget* parent /*=0*/ )
22 {
23  da_model = dmodel;
24  tw_recs = treewidg;
25  parentw = (QWidget*)parent;
27 
28  QStringList theads;
29 
30  theads << "Type" << "Label" << "SubType" << "Source"
31  << "Children\nDB, Local" << "Descendants\nDB, Local";
32  ntrows = 5;
33  ntcols = theads.size();
34  tw_recs->setHeaderLabels( theads );
35  tw_recs->setFont( QFont( US_Widgets::fixedFont().family(),
36  US_GuiSettings::fontSize() - 1 ) );
37  tw_recs->setObjectName( QString( "tree-widget" ) );
38  tw_recs->setAutoFillBackground( true );
39 }
40 
41 // show or hide (expand/collapse) all rows of specified type
42 void US_DataTree::toggle_expand( QString c1str, bool show )
43 {
44  QList< QTreeWidgetItem* > listi = tw_recs->findItems(
45  c1str, Qt::MatchExactly | Qt::MatchWrap | Qt::MatchRecursive, 0 );
46 
47 DbgLv(1) << "DT: togl_exp str show" << c1str << show;
48  for ( int ii = 0; ii < listi.size(); ii++ )
49  listi.at( ii )->setExpanded( show );
50 }
51 
52 // build the data tree from descriptions read
54 {
55  QString rtyp;
56  QString subt;
57  QString labl;
58  QString dguid;
59  QString rsrc;
60  QString anch;
61  QString ande;
62  int ityp = 1;
63  int nchdb = 0;
64  int nchlo = 0;
65  int ndedb = 0;
66  int ndelo = 0;
67  QBrush fbru( colorBrown );
68  QBrush bbru( colorWhite );
69  QTreeWidgetItem* pitems[4];
70  US_Passwd pw;
71  US_DB2 db( pw.getPasswd() );
72  US_DB2* dbP = &db;
73 
75 
79  ntrows = ncrecs;
80  ncraws = ncedts = ncmods = ncnois = 0;
81  ndraws = ndedts = ndmods = ndnois = 0;
82  nlraws = nledts = nlmods = nlnois = 0;
83  tw_recs->clear();
84  QProgressBar* progress = da_model->progrBar();
85  progress->setMaximum( ncrecs );
86  progress->setValue ( 0 );
87 
88  for ( int ii = 0; ii < ncrecs; ii++ )
89  {
90  cdesc = da_model->row_datadesc( ii );
91  ityp = cdesc.recType;
92  QStringList cvals;
93  bool isDba = ( ( cdesc.recState & US_DataModel::REC_DB ) != 0 );
94  bool isLoc = ( ( cdesc.recState & US_DataModel::REC_LO ) != 0 );
95 
96  if ( cdesc.recordID < 0 && isLoc )
97  { // local only
98  fbru = QBrush( colorBrown );
99  rsrc = "Local";
100 
101  if ( ityp == 1 )
102  {
103  nlraws++; ncraws++;
104  rtyp = "Raw";
105  }
106  else if ( ityp == 2 )
107  {
108  nledts++; ncedts++;
109  rtyp = "Edited";
110  }
111  else if ( ityp == 3 )
112  {
113  nlmods++; ncmods++;
114  rtyp = "Model";
115  }
116  else if ( ityp == 4 )
117  {
118  nlnois++; ncnois++;
119  rtyp = "Noise";
120  }
121  else
122  {
123  rtyp = "none";
124  ityp = 0;
125  }
126  }
127 
128  else if ( isDba && !isLoc )
129  { // database only
130  fbru = QBrush( colorBlue );
131  rsrc = "DB";
132 
133  if ( ityp == 1 )
134  {
135  ndraws++; ncraws++;
136  rtyp = "Raw";
137  }
138  else if ( ityp == 2 )
139  {
140  ndedts++; ncedts++;
141  rtyp = "Edited";
142  }
143  else if ( ityp == 3 )
144  {
145  ndmods++; ncmods++;
146  rtyp = "Model";
147  }
148  else if ( ityp == 4 )
149  {
150  ndnois++; ncnois++;
151  rtyp = "Noise";
152  }
153  else
154  {
155  rtyp = "none";
156  ityp = 0;
157  }
158  }
159 
160  else
161  { // both local and database
162  fbru = QBrush( colorGreen );
163  rsrc = "In Sync";
164 
165  if ( ! cdesc.contents.isEmpty() )
166  {
167  QString cont1 = cdesc.contents.section( " ", 0, 1 ).simplified();
168  QString cont2 = cdesc.contents.section( " ", 2, 4 ).simplified();
169  if ( cont1 != cont2 && significant_diffs( cdesc, dbP ) )
170  {
171  fbru = QBrush( colorRed );
172  rsrc = "Conflict";
173 DbgLv(1) << "CONFLICT cont1 cont2" << cont1 << cont2;
174  }
175  }
176 
177  if ( ityp == 1 )
178  {
179  nlraws++; ndraws++; ncraws++;
180  rtyp = "Raw";
181  }
182  else if ( ityp == 2 )
183  {
184  nledts++; ndedts++; ncedts++;
185  rtyp = "Edited";
186  }
187  else if ( ityp == 3 )
188  {
189  nlmods++; ndmods++; ncmods++;
190  rtyp = "Model";
191  }
192  else if ( ityp == 4 )
193  {
194  nlnois++; ndnois++; ncnois++;
195  rtyp = "Noise";
196  }
197  else
198  {
199  rtyp = "none";
200  ityp = 0;
201  rsrc = "Conflict";
202  }
203  }
204 
205  labl = cdesc.label;
206  dguid = cdesc.dataGUID;
207  subt = cdesc.subType;
208  nchdb = nchlo = ndedb = ndelo = 0;
209 
211  { // mark artificial record with color and source text
212  fbru = QBrush( colorGray );
213  rsrc = "dummy";
214  }
215 
216  for ( int jj = ( ii + 1 ); jj < ncrecs; jj++ )
217  { // count children and descendants from next-row until back-to-level
219 
220  if ( ddesc.recType <= ityp )
221  break; // once we've reached or passed same level, break
222 
223  // from database?
224  bool isDba = ( ( ddesc.recState & US_DataModel::REC_DB ) != 0 );
225  // from local?
226  bool isLoc = ( ( ddesc.recState & US_DataModel::REC_LO ) != 0 );
227  // direct child?
228  //bool child = ( ddesc.parentGUID == dguid );
229  bool child = ( ( ddesc.recType - ityp ) == 1 );
230 
231  ndedb += ( isDba ) ? 1 : 0; // bump db descendant count
232  nchdb += ( isDba && child ) ? 1 : 0; // bump db child count
233  ndelo += ( isLoc ) ? 1 : 0; // bump local descendant count
234  nchlo += ( isLoc && child ) ? 1 : 0; // bump local child count
235  }
236 
237  anch = ( ityp < 4 ) ?
238  QString( "%1, %2" ).arg( nchdb ).arg( nchlo ) : ""; // children
239  ande = ( ityp < 3 ) ?
240  QString( "%1, %2" ).arg( ndedb ).arg( ndelo ) : ""; // descendants
241 
242  QTreeWidgetItem* item;
243  int wiutype = (int)QTreeWidgetItem::UserType + ii; // type: encoded index
244 
245  cvals << rtyp << labl << subt << rsrc << anch << ande;
246 
247  if ( ityp == 1 )
248  { // Raws are children of the root
249  item = new QTreeWidgetItem( tw_recs, cvals, wiutype );
250  }
251 
252  else
253  { // others are children of the next level up
254  item = new QTreeWidgetItem( pitems[ ityp - 2 ], cvals, wiutype );
255  }
256 
257  pitems[ ityp - 1 ] = item; // save next parent of this type
258 
259  for ( int jj = 0; jj < ntcols; jj++ )
260  {
261  item->setForeground( jj, fbru );
262  item->setBackground( jj, bbru );
263  }
264 
265  progress->setValue( ii + 1 );
266  qApp->processEvents();
267  }
268 
269  // resize so all of columns are shown
270  tw_recs->expandAll(); // expand the entire tree
271 
272  for ( int jj = 0; jj < ntcols; jj++ )
273  {
274  tw_recs->resizeColumnToContents( jj ); // resize to fit contents
275  }
276 
277  tw_recs->collapseAll(); // collapse the entire tree
278 }
279 
280 // set up and display a row context menu
281 void US_DataTree::row_context_menu( QTreeWidgetItem* item )
282 {
283  selitems = tw_recs->selectedItems();
284  int nsel = selitems.size();
285 DbgLv(1) << " context_menu nbr sel rows" << selitems.size();
286  tw_item = item;
287  int irow = item->type() - (int)QTreeWidgetItem::UserType;
288 
289  if ( nsel > 1 )
290  { // If multiple selections, set first row
291  for ( int ii = 0; ii < nsel; ii++ )
292  {
293  QTreeWidgetItem* jitem = selitems[ ii ];
294  int jrow = jitem->type() - (int)QTreeWidgetItem::UserType;
295  irow = qMin( irow, jrow );
296  }
297  }
298 
299 DbgLv(2) << " context_menu row" << irow+1;
300  da_model->setCurrent( irow );
301 DbgLv(2) << " context_menu RTN setCurrent";
303 DbgLv(2) << " context_menu RTN current_datadesc";
304 
305  QString tupload = tr( " upload record to DB" );
306  QString tdnload = tr( " download record to local" );
307  QString tdbasrm = tr( " remove branch from DB" );
308  QString tloclrm = tr( " remove branch from local" );
309  QString tbothrm = tr( " remove branch from both" );
310  QString tshdeta = tr( " show record details" );
311 
312  if ( nsel > 1 )
313  { // Multiple selections: "record/branch" to "branches"
314  tupload.replace( tr( "record" ), tr( "branches" ) );
315  tdnload.replace( tr( "record" ), tr( "branches" ) );
316  tdbasrm.replace( tr( "branch" ), tr( "branches" ) );
317  tloclrm.replace( tr( "branch" ), tr( "branches" ) );
318  tbothrm.replace( tr( "branch" ), tr( "branches" ) );
319  tshdeta.replace( tr( "record" ), tr( "records" ) );
320  }
321 
322  if ( cdesc.recType == 1 )
323  { // Raw: "record/branches" to "descendants"
324  tupload.replace( tr( "record" ), tr( "descendants" ) );
325  tupload.replace( tr( "branches" ), tr( "descendants" ) );
326  tdnload.replace( tr( "record" ), tr( "descendants" ) );
327  tdnload.replace( tr( "branches" ), tr( "descendants" ) );
328  tdbasrm.replace( tr( "branches" ), tr( "descendants" ) );
329  tdbasrm.replace( tr( "branch" ), tr( "descendants" ) );
330  tloclrm.replace( tr( "branches" ), tr( "descendants" ) );
331  tloclrm.replace( tr( "branch" ), tr( "descendants" ) );
332  tbothrm.replace( tr( "branches" ), tr( "descendants" ) );
333  tbothrm.replace( tr( "branch" ), tr( "descendants" ) );
334  }
335 
336  QMenu* cmenu = new QMenu();
337  QAction* aupload = new QAction( tupload, parentw );
338  QAction* adnload = new QAction( tdnload, parentw );
339  QAction* adbasrm = new QAction( tdbasrm, parentw );
340  QAction* aloclrm = new QAction( tloclrm, parentw );
341  QAction* abothrm = new QAction( tbothrm, parentw );
342  QAction* ashdeta = new QAction( tshdeta, parentw );
343 
344  cmenu->addAction( aupload );
345  cmenu->addAction( adnload );
346  cmenu->addAction( adbasrm );
347  cmenu->addAction( aloclrm );
348  cmenu->addAction( abothrm );
349  cmenu->addAction( ashdeta );
350 DbgLv(2) << " context_menu RTN addAction";
351 
352  connect( aupload, SIGNAL( triggered() ),
353  this, SLOT( item_upload() ) );
354  connect( adnload, SIGNAL( triggered() ),
355  this, SLOT( item_download() ) );
356  connect( adbasrm, SIGNAL( triggered() ),
357  this, SLOT( item_remove_db() ) );
358  connect( aloclrm, SIGNAL( triggered() ),
359  this, SLOT( item_remove_loc() ) );
360  connect( abothrm, SIGNAL( triggered() ),
361  this, SLOT( item_remove_all() ) );
362  connect( ashdeta, SIGNAL( triggered() ),
363  this, SLOT( item_details() ) );
364 
365  // disable menu items that are not appropriate to the record
366 
367  if ( ( cdesc.recState & US_DataModel::REC_LO ) == 0 )
368  { // if record not local, no upload and no remove from local or both
369  aupload->setEnabled( false );
370  aloclrm->setEnabled( false );
371  abothrm->setEnabled( false );
372  }
373 
374  if ( ( cdesc.recState & US_DataModel::REC_DB ) == 0 )
375  { // if record not db, no download and no remove from db or both
376  adnload->setEnabled( false );
377  adbasrm->setEnabled( false );
378  abothrm->setEnabled( false );
379  }
380 
381  // Disable upload if parent not in the DB
382  int jrow = irow;
384 
385  while ( --jrow > 0 && cdesc.recType > 1 )
386  {
387  jdesc = da_model->row_datadesc( jrow );
388  if ( jdesc.recType < cdesc.recType )
389  break;
390  }
391 
392  if ( ( jdesc.recState & US_DataModel::REC_DB ) == 0 )
393  aupload->setEnabled( false );
394 
395  // display the context menu and act on selection
396 DbgLv(2) << " context_menu CALL cmenu exec";
397  cmenu->exec( QCursor::pos() );
398 
399 }
400 
401 // open a dialog and display data tree help
403 {
404  QString fileexts = tr( "Text,Log files (*.txt *.log);;" )
405  + tr( "All files (*)" );
406  QString mtext =
407  tr( "Data Tree Columns --\n\n" ) +
408  tr( " \"Type\" : Type of data set record -\n" ) +
409  tr( " \"Raw\", \"Edited\", \"Model\" or \"Noise\".\n") +
410  tr( " \"Label\" : Short description of specific record.\n" ) +
411  tr( " \"SubType\" : Record-specific type (e.g. \"2DSA\", \"TI\").\n")+
412  tr( " \"Source\" : Location/state (see color legend below)-\n" ) +
413  tr( " \"DB\" (Blue, database); \n" ) +
414  tr( " \"Local\" (Brown, local disk); \n" ) +
415  tr( " \"In Sync\" (Green, both, consistent); \n" ) +
416  tr( " \"Conflict\" (Red, both, inconsistent); \n" ) +
417  tr( " \"dummy\" (Gray, missing parent filler).\n") +
418  tr( " \"Children : Number of Children of this row\n" ) +
419  tr( " DB, Local\" from Database, Local-disk.\n" ) +
420  tr( " \"Descendants : Number of Descendants of this row\n" ) +
421  tr( " DB, Local\" from Database, Local-disk.\n\n" ) +
422  tr( "Tree Row Color Legend --\n\n" ) +
423  tr( " Blue : This record exists for database only.\n" ) +
424  tr( " Brown : This record exists for local disk only.\n" ) +
425  tr( " Green : This record exists and is consistent\n" ) +
426  tr( " for both database and local.\n" ) +
427  tr( " Red : This record exists for both, but is inconsistent.\n")+
428  tr( " Gray : This record is a filler for a missing parent.\n\n" ) +
429  tr( "Tree Entry Processes --\n\n" ) +
430  tr( " A right-mouse-button click on any cell of a row pops up\n" ) +
431  tr( " a context menu with actions to take for the record.\n" ) +
432  tr( " Menu items will only be enabled if the action is\n" ) +
433  tr( " appropriate for the particular record shown in the row.\n" ) +
434  tr( " Possible actions are:\n" ) +
435  tr( " \"upload to DB\"\n" ) +
436  tr( " -> Upload this local record to the database;\n" ) +
437  tr( " \"download to local\"\n" ) +
438  tr( " -> Download this DB record to local disk.\n" ) +
439  tr( " \"remove from DB\"\n" ) +
440  tr( " -> Remove this record from the database.\n" ) +
441  tr( " \"remove from local\"\n" ) +
442  tr( " > Remove this record from local disk.\n" ) +
443  tr( " \"remove both\"\n" ) +
444  tr( " -> Remove this record from both DB and local.\n" ) +
445  tr( " \"show details\"\n" ) +
446  tr( " -> Pop up a text dialog with details about the record.\n" ) +
447  tr( "Detailed Help --\n\n" ) +
448  tr( " Click on the \"Help\" button for standard documentation,\n" ) +
449  tr( " including sample images and details on all GUI elements.\n" );
450 
451  US_Editor* editd = new US_Editor( US_Editor::LOAD, true, fileexts );
452  editd->setWindowTitle( tr( "Data Sets Tree Help and Legend" ) );
453  editd->move( QCursor::pos() + QPoint( 200, 200 ) );
454  editd->resize( 600, 500 );
455  editd->e->setFont( QFont( US_Widgets::fixedFont().family(),
457  editd->e->setText( mtext );
458  editd->show();
459 }
460 
461 // perform item upload action
463 {
464  QMessageBox msgBox( parentw );
465  QString item_exs = tr( "Local disk only" );
466  QString item_act = tr( "DB create" );
467 DbgLv(2) << "ITEM Upload";
468  int irow = tw_item->type() - (int)QTreeWidgetItem::UserType;
469  da_model->setCurrent( irow );
471 
472  if ( ( cdesc.recState & US_DataModel::REC_DB ) != 0 )
473  {
474  item_exs = tr( "both DB and Local" );
475  item_act = tr( "DB replace" );
476  }
477 
478  record_type( cdesc.recType, item_act );
479 
480  msgBox.setWindowTitle( item_act );
481  msgBox.setTextFormat( Qt::RichText );
482  msgBox.setText( action_text( item_exs, item_act ) );
483  msgBox.addButton( QMessageBox::No );
484  msgBox.addButton( QMessageBox::Yes );
485  msgBox.setDefaultButton( QMessageBox::No );
486 
487  if ( msgBox.exec() == QMessageBox::Yes )
488  {
489 DbgLv(2) << " ITEM ACTION: YES";
490  int stat1 = do_actions( item_exs, item_act );
491 
492  action_result( stat1, item_act );
493  }
494 
495  else
496  {
497 DbgLv(2) << " ITEM ACTION: NO";
498  action_result( 999, item_act );
499  }
500 
501 }
502 
503 // perform item download action
505 {
506  QMessageBox msgBox( parentw );
507  QString item_exs = tr( "Database only" );
508  QString item_act = tr( "Local file create" );
509 DbgLv(2) << "ITEM Download";
510  int irow = tw_item->type() - (int)QTreeWidgetItem::UserType;
511  da_model->setCurrent( irow );
513 
514  if ( ( cdesc.recState & US_DataModel::REC_LO ) != 0 )
515  {
516  item_exs = tr( "both DB and Local" );
517  item_act = tr( "Local file replace" );
518  }
519 
520  record_type( cdesc.recType, item_act );
521 
522  msgBox.setWindowTitle( item_act );
523  msgBox.setTextFormat( Qt::RichText );
524  msgBox.setText( action_text( item_exs, item_act ) );
525  msgBox.addButton( QMessageBox::No );
526  msgBox.addButton( QMessageBox::Yes );
527  msgBox.setDefaultButton( QMessageBox::No );
528 
529  if ( msgBox.exec() == QMessageBox::Yes )
530  {
531 DbgLv(2) << " ITEM ACTION: YES";
532  int stat1 = do_actions( item_exs, item_act );
533 
534  action_result( stat1, item_act );
535  }
536 
537  else
538  {
539 DbgLv(2) << " ITEM ACTION: NO";
540  action_result( 999, item_act );
541  }
542 }
543 
544 // perform item remove-from-db action
546 {
547 DbgLv(2) << "ITEM Remove from DB";
548  QString item_exs = tr( "Database only" );
549  QString item_act = tr( "DB-only remove" );
550  QMessageBox msgBox( parentw );
551  int irow = tw_item->type() - (int)QTreeWidgetItem::UserType;
552  da_model->setCurrent( irow );
554 
555  record_type( cdesc.recType, item_act );
556 
557  msgBox.setWindowTitle( item_act );
558  msgBox.setTextFormat( Qt::RichText );
559  msgBox.setText( action_text( item_exs, item_act ) );
560  msgBox.addButton( QMessageBox::No );
561  msgBox.addButton( QMessageBox::Yes );
562  msgBox.setDefaultButton( QMessageBox::No );
563 
564  if ( msgBox.exec() == QMessageBox::Yes )
565  {
566 DbgLv(2) << " ITEM ACTION: YES";
567  int stat1 = do_actions( item_exs, item_act );
568 
569  action_result( stat1, item_act );
570  }
571 
572  else
573  {
574 DbgLv(2) << " ITEM ACTION: NO";
575  action_result( 999, item_act );
576  }
577 }
578 
579 // perform item remove-from-local action
581 {
582 DbgLv(2) << "ITEM Remove from Local";
583  QString item_exs = tr( "Local disk only" );
584  QString item_act = tr( "Local-only remove" );
585  QMessageBox msgBox( parentw );
586  int irow = tw_item->type() - (int)QTreeWidgetItem::UserType;
587  da_model->setCurrent( irow );
589 
590  record_type( cdesc.recType, item_act );
591 
592  msgBox.setWindowTitle( item_act );
593  msgBox.setTextFormat( Qt::RichText );
594  msgBox.setText( action_text( item_exs, item_act ) );
595  msgBox.addButton( QMessageBox::No );
596  msgBox.addButton( QMessageBox::Yes );
597  msgBox.setDefaultButton( QMessageBox::No );
598 
599  if ( msgBox.exec() == QMessageBox::Yes )
600  {
601 DbgLv(2) << " ITEM ACTION: YES";
602  int stat1 = do_actions( item_exs, item_act );
603 
604  action_result( stat1, item_act );
605  }
606 
607  else
608  {
609 DbgLv(2) << " ITEM ACTION: NO";
610  action_result( 999, item_act );
611  }
612 }
613 
614 // perform item remove-from-all action
616 {
617 DbgLv(2) << "ITEM Remove Both DB and Local";
618  QString item_exs = tr( "both DB and Local" );
619  QString item_act = tr( "DB+Local remove" );
620  QMessageBox msgBox( parentw );
621  int irow = tw_item->type() - (int)QTreeWidgetItem::UserType;
622  da_model->setCurrent( irow );
624 
625  record_type( cdesc.recType, item_act );
626 
627  msgBox.setWindowTitle( item_act );
628  msgBox.setTextFormat( Qt::RichText );
629  msgBox.setText( action_text( item_exs, item_act ) );
630  msgBox.addButton( QMessageBox::No );
631  msgBox.addButton( QMessageBox::Yes );
632  msgBox.setDefaultButton( QMessageBox::No );
633 
634  if ( msgBox.exec() == QMessageBox::Yes )
635  {
636 DbgLv(2) << " ITEM ACTION: YES";
637  int stat1 = do_actions( item_exs, item_act );
638 
639  action_result( stat1, item_act );
640  }
641 
642  else
643  {
644 DbgLv(2) << " ITEM ACTION: NO";
645  action_result( 999, item_act );
646  }
647 }
648 
649 // Perform item details action
651 {
652  if ( selitems.size() > 1 )
653  {
654  items_details();
655  return;
656  }
657 
658  const char* rtyps[] = { "RawData", "EditedData", "Model", "Noise" };
659  QString fileexts = tr( "Text,Log files (*.txt *.log)"
660  ";;All files (*)" );
661  int irow = tw_item->type() - (int)QTreeWidgetItem::UserType;
662 DbgLv(2) << "DT: i_details row" << irow;
663 
664  da_model->setCurrent( irow++ ); // get description record, index as 1...
666 
667  QString mtext =
668  tr( "Data Tree Item at Row %1 -- \n\n" ).arg( irow ) +
669  tr( " Type : %1 (%2)\n" )
670  .arg( cdesc.recType ).arg( rtyps[ cdesc.recType - 1 ] ) +
671  tr( " SubType : " ) + cdesc.subType + "\n" +
672  tr( " Label : " ) + cdesc.label + "\n" +
673  tr( " Description : " ) + cdesc.description + "\n" +
674  tr( " DB record ID : %1\n" ).arg( cdesc.recordID ) +
675  tr( " Global ID : " ) + cdesc.dataGUID + "\n" +
676  tr( " Parent GUID : " ) + cdesc.parentGUID + "\n" +
677  tr( " DB parent ID : %1\n" ).arg( cdesc.parentID ) +
678  tr( " File Directory : " )
679  + cdesc.filename.section( "/", 0, -2 ) + "\n" +
680  tr( " File Name : " )
681  + cdesc.filename.section( "/", -1, -1 ) + "\n" +
682  tr( " File Last Mod : " ) + cdesc.filemodDate + "\n" +
683  tr( " DB Last Mod : " ) + cdesc.lastmodDate + "\n" +
684  tr( " Record State : " ) + record_state( cdesc.recState ) + "\n";
685 
686  if ( cdesc.contents.length() < 60 )
687  {
688  mtext = mtext +
689  tr( " Content Checks : " ) + cdesc.contents;
690  }
691  else
692  {
693  QString cont1 = cdesc.contents.section( " ", 0, 1 ).simplified();
694  QString cont2 = cdesc.contents.section( " ", 2, 4 ).simplified();
695  mtext = mtext +
696  tr( " Content Checks : " ) + cont1 + "\n" +
697  " " + cont2 + "\n";
698 #if 0
699 if (cdesc.recType==3 && cont1!=cont2) {
700 US_Passwd pw;
701 US_DB2* dbP = new US_DB2( pw.getPasswd() );
702 US_Model model1;
703 US_Model model2;
704 QString tmpdir=US_Settings::tmpDir();
705 model1.load( QString::number(cdesc.recordID), dbP );
706 model2.load( cdesc.filename );
707 QString fname1=tmpdir+"/model1d.xml";
708 QString fname2=tmpdir+"/model2f.xml";
709 model1.write(fname1);
710 model2.write(fname2);
711 QString fname3=tmpdir+"/model3d.xml";
712 QString fname4=tmpdir+"/model4f.xml";
713 model1.update_coefficients();
714 model2.update_coefficients();
715 model1.write(fname3);
716 model2.write(fname4);
717 }
718 #endif
719  }
720 
721  // display the text dialog
722  US_Editor* editd = new US_Editor( US_Editor::LOAD, true, fileexts );
723  editd->setWindowTitle( tr( "Data Tree Entry Details" ) );
724  editd->move( QCursor::pos() + QPoint( 100, 100 ) );
725  editd->resize( 720, 360 );
726  editd->e->setFont( QFont( US_Widgets::fixedFont().family(),
728  editd->e->setText( mtext );
729  editd->show();
730 }
731 
732 // Perform multiple-items details action
734 {
735  const char* rtyps[] = { "RawData", "EditedData", "Model", "Noise" };
736  QString fileexts = tr( "Text,Log files (*.txt *.log)"
737  ";;All files (*)" );
738 
739  int narows = action_rows();
740 
741  int nsrows = selrows.size();
742  int nrrows = rawrows.size();
743  int irow = selrows[ 0 ];
744  int krow = selrows[ nsrows - 1 ];
745  krow = ( narows > 0 ) ? actrows[ narows - 1 ] : krow;
746 DbgLv(2) << "DT: i_details row" << irow;
747  cdesc = da_model->row_datadesc( irow );
749 
750  QString mtext =
751  tr( "Data Tree Items from Rows %1 to %2 -- \n\n" )
752  .arg( irow ).arg( krow ) +
753  tr( " Type (first) : %1\n" ).arg( rtyps[ cdesc.recType - 1 ] ) +
754  tr( " Description : " ) + cdesc.description + "\n" +
755  tr( " DB record ID : %1\n" ).arg( cdesc.recordID ) +
756  tr( " File Directory : " )
757  + cdesc.filename.section( "/", 0, -2 ) + "\n" +
758  tr( " File Name : " )
759  + cdesc.filename.section( "/", -1, -1 ) + "\n" +
760  tr( " Type (last) : %1\n" ).arg( rtyps[ kdesc.recType - 1 ] ) +
761  tr( " Description : " ) + kdesc.description + "\n" +
762  tr( " DB record ID : %1\n" ).arg( kdesc.recordID ) +
763  tr( " File Directory : " )
764  + kdesc.filename.section( "/", 0, -2 ) + "\n" +
765  tr( " File Name : " )
766  + kdesc.filename.section( "/", -1, -1 ) + "\n" +
767  tr( " Total Selected Records : " ) + QString::number( nsrows ) + "\n" +
768  tr( " Total Action Records : " ) + QString::number( narows ) + "\n" +
769  tr( " Total Raw Selections : " ) + QString::number( nrrows ) + "\n";
770 
771  // display the text dialog
772  US_Editor* editd = new US_Editor( US_Editor::LOAD, true, fileexts );
773  editd->setWindowTitle( tr( "Data Tree Entries Details" ) );
774  editd->move( QCursor::pos() + QPoint( 100, 100 ) );
775  editd->resize( 760, 320 );
776  editd->e->setFont( QFont( US_Widgets::fixedFont().family(),
778  editd->e->setText( mtext );
779  editd->show();
780 }
781 
782 // Prepend a record type string to an item action
783 void US_DataTree::record_type( int recType, QString& item_act )
784 {
785  const char* rtyps[] = { "RawData", "EditedData", "Model", "Noise" };
786  int sizert = sizeof( rtyps ) / sizeof( rtyps[ 0 ] );
787 
788  if ( recType > 0 && recType <= sizert )
789  {
790  item_act = QString( rtyps[ recType - 1 ] ) + " " + item_act;
791  }
792 }
793 
794 // Format an item action text for a message box
795 QString US_DataTree::action_text( QString exstext, QString acttext )
796 {
797  QString lines = ( selitems.size() == 1 )
798  ? tr( "This item exists on %1.<br>"
799  "Are you sure you want to proceed with a %2?<ul>" )
800  .arg( exstext ).arg( acttext )
801  : tr( "These items exists on %1.<br>"
802  "Are you sure you want to proceed with %2s?<ul>" )
803  .arg( exstext ).arg( acttext );
804  return lines +
805  tr( "<li><b>No </b> to cancel the action;</li>"
806  "<li><b>Yes</b> to proceed with the action.</li></ul>" );
807 }
808 
809 // report the result of an item action
810 void US_DataTree::action_result( int stat, QString item_act )
811 {
812  QLabel* lb_status = da_model->statlab();
813 
814  if ( stat != 999 )
815  { // proceed was selected: test status of action
816 
817  if ( stat == 0 )
818  { // action was successful
819  lb_status->setText( tr( "\"%1\" Success!" ).arg( item_act ) );
820  }
821 
822  else
823  { // action got an error
824  QMessageBox::warning( parentw,
825  item_act + tr( " *ERROR*!" ),
826  tr( "The \"%1\" action had an error: %2" )
827  .arg( item_act ).arg( stat )
828  + "\n\n" + da_process->lastError() );
829  lb_status->setText( tr( "\"%1\" ERROR!" ).arg( item_act ) );
830  }
831  }
832 
833  else
834  { // cancel was selected: report it
835  lb_status->setText( tr( "\"%1\" Cancelled!" ).arg( item_act ) );
836  }
837 }
838 
839 // compose string describing record state
840 QString US_DataTree::record_state( int istate )
841 {
842  QString hexn = QString().sprintf( "0x%3.3x", istate );
843 
844  QString flags = "NOSTAT"; // by default, no state
845 
846  if ( istate & US_DataModel::REC_DB )
847  flags = flags + "|REC_DB"; // record exists in db
848 
849  if ( istate & US_DataModel::REC_LO )
850  flags = flags + "|REC_LO"; // record exists locally
851 
852  if ( istate & US_DataModel::PAR_DB )
853  flags = flags + "|PAR_DB"; // parent exists in db
854 
855  if ( istate & US_DataModel::PAR_LO )
856  flags = flags + "|PAR_LO"; // parent exists locally
857 
858  if ( istate & US_DataModel::HV_DET )
859  flags = flags + "|DETAIL"; // content details are supplied
860 
861  if ( istate & US_DataModel::IS_CON )
862  flags = flags + "|CONSIS"; // record is consistent in db+local
863 
864  if ( istate & US_DataModel::ALL_OK )
865  flags = flags + "|ALL_OK"; // record is ok in all respects
866 
867  if ( flags != "NOSTAT" )
868  flags = flags.mid( 7, 999 ); // remove any "NOSTAT|"
869 
870  return "(" + hexn + ") " + flags; // return hex flag and text version
871 }
872 
873 // Perform the action(s) chosen in context menu
874 int US_DataTree::do_actions( QString item_exs, QString item_act )
875 {
876  int narows = action_rows();
877 DbgLv(1) << "ITEM do_actions" << narows << item_exs << item_act;
878  int naerrs = 0;
879  int istat = 0;
880  bool frDB = item_exs.contains( "Datab" )
881  || item_exs.contains( "DB " );
882  bool frLoc = item_exs.contains( "Local" );
883  bool frBoth = item_exs.contains( "both" );
884  frDB = frBoth ? true : frDB;
885  frLoc = frBoth ? true : frLoc;
886  bool dnLoad = item_act.contains( "Local file " );
887  bool upLoad = item_act.contains( "DB create" )
888  || item_act.contains( "DB replace" );
889  bool remove = item_act.contains( "remove" );
890  QProgressBar* progress = da_model->progrBar();
891  QLabel* stlabel = da_model->statlab();
892 
893  if ( remove && frDB )
894  { // REMOVE from DB
895  int lrtyp = 9; // last remove type
896  int karows = 0;
897 
898  for ( int jj = 0; jj < narows; jj++ )
899  {
900  int irow = actrows[ jj ];
902  int ityp = ddesc.recType;
903 DbgLv(1) << "RMV_REC: irow ityp lrtyp" << irow << ityp << lrtyp;
904 
905  if ( ityp > lrtyp )
906  { // Just mark as deleted if descendant of last removed
907  ddesc.recordID = -1;
908  da_model->change_datadesc( ddesc, irow );
909  continue;
910  }
911 
912  lrtyp = ityp; // save type of last removed
913  karows++;
914  int stat1 = da_process->record_remove_db( irow );
915 DbgLv(1) << "RMV_REC: karows stat1" << karows << stat1;
916 
917  if ( stat1 != 0 )
918  {
919  naerrs++;
920  istat = stat1;
921  }
922  }
923 
924  narows = karows;
925  }
926 
927  if ( remove && frLoc )
928  { // REMOVE from LOCAL
929  for ( int ii = 4; ii > 1; ii-- )
930  { // Remove records from bottom up
931  for ( int jj = 0; jj < narows; jj++ )
932  {
933  int irow = actrows[ jj ];
935  int ityp = ddesc.recType;
936 
937  if ( ityp != ii ) continue; // skip if not at current level
938 
939  int stat1 = da_process->record_remove_local( irow );
940 
941  if ( stat1 != 0 )
942  {
943  naerrs++;
944  istat = stat1;
945  }
946  }
947  }
948  }
949 
950  if ( upLoad )
951  { // UPLOAD to DB
952  for ( int ii = 2; ii < 5; ii++ )
953  { // Upload records from top down
954  for ( int jj = 0; jj < narows; jj++ )
955  {
956  int irow = actrows[ jj ];
958  int ityp = ddesc.recType;
959 
960  if ( ityp != ii ) continue; // skip if not at current level
961 
962  int stat1 = da_process->record_upload( irow );
963 
964  if ( stat1 != 0 )
965  {
966  naerrs++;
967  istat = stat1;
968  }
969  }
970  }
971  }
972 
973  if ( dnLoad )
974  { // DOWNLOAD to LOCAL
975 QTime timer;
976 timer.start();
977  stlabel->setText( tr( "Downloading records to local disk..." ) );
978  progress->setMaximum( narows );
979  progress->setValue ( 0 );
980  int ndown = 0;
981  for ( int ii = 2; ii < 5; ii++ )
982  { // Download records from top down
983  for ( int jj = 0; jj < narows; jj++ )
984  {
985  int irow = actrows[ jj ];
987  int ityp = ddesc.recType;
988 
989  if ( ityp != ii ) continue; // skip if not at current level
990 
991  int stat1 = da_process->record_download( irow );
992 
993  if ( stat1 != 0 )
994  {
995  naerrs++;
996  istat = stat1;
997  }
998  progress->setValue( ++ndown );
999  }
1000 qDebug() << "DT: DwnLd: ii" << ii << "time" << timer.elapsed();
1001  }
1002  }
1003 
1004  if ( naerrs > 0 && naerrs < narows )
1005  { // Append to error message, explaining partial success
1006  da_process->partialError( naerrs, narows );
1007  }
1008 
1009  return istat;
1010 }
1011 
1012 // Create lists of selected rows and implied action rows
1014 {
1015  // Build up the list of selected rows; then sort it
1016  int nsrows = selitems.size();
1017  selrows.clear();
1018  actrows.clear();
1019  rawrows.clear();
1020 
1021  for ( int ii = 0; ii < nsrows; ii++ )
1022  selrows << selitems[ ii ]->type() - (int)QTreeWidgetItem::UserType;
1023 
1024  qSort( selrows );
1025 DbgLv(1) << "acrow: nsrows" << nsrows;
1026 
1027  // Build up the list of action rows; include descendants, exclude Raw
1028  for ( int ii = 0; ii < nsrows; ii++ )
1029  {
1030  int irow = selrows[ ii ];
1031  US_DataModel::DataDesc ddesc = da_model->row_datadesc( irow );
1032  int ityp = ddesc.recType;
1033 DbgLv(1) << "acrow: irow ityp" << irow << ityp;
1034  if ( ityp > 1 )
1035  actrows << irow; // add to action list if not Raw
1036  else
1037  rawrows << irow; // otherwise, add to Raw-selected list
1038 
1039  for ( int jrow = ( irow + 1 ); jrow < ncrecs; jrow++ )
1040  { // add descendants from next-row until back-to-level
1041  US_DataModel::DataDesc ddesc = da_model->row_datadesc( jrow );
1042 
1043 DbgLv(1) << "acrow: jrow jtyp" << jrow << ddesc.recType;
1044  if ( ddesc.recType <= ityp )
1045  break; // once we've reached or passed same level, break
1046 
1047  if ( selrows.contains( jrow ) )
1048  continue; // if descendant already in select list, don't add
1049 
1050  actrows << jrow;
1051  }
1052  }
1053 DbgLv(1) << "acrow: narows nrrows" << actrows.size() << rawrows.size();
1054 
1055  return actrows.size();
1056 }
1057 
1058 // Check model or noise for significant differences
1060  US_DB2* dbP )
1061 {
1062  bool differs = true; // Assume significant differences unless model,noise
1063  const double dtoler = 5.0e-5;
1064 
1065  // If model, check for differences beyond tolerance in component values
1066  if ( ddesc.recType == 3 )
1067  {
1068  US_Model dmodel;
1069  US_Model fmodel;
1070  double difmax = 0.0;
1071 
1072  int dst = dmodel.load( QString::number( ddesc.recordID ), dbP );
1073  int fst = fmodel.load( ddesc.filename );
1074 
1075  if ( dst != US_DB2::OK )
1076  dmodel.description = "DBAD:" + dmodel.description;
1077 
1078  if ( fst != US_DB2::OK )
1079  fmodel.description = "FBAD:" + fmodel.description;
1080 
1081 DbgLv(1) << " CONFLICT? dst fst differs" << dst << fst << differs;
1082  if ( dmodel == fmodel )
1083  differs = false; // Differences only in formatting
1084 
1085  else if ( ( dmodel.monteCarlo == fmodel.monteCarlo ) &&
1086  ( dmodel.description == fmodel.description ) &&
1087  ( dmodel.modelGUID == fmodel.modelGUID ) &&
1088  ( dmodel.editGUID == fmodel.editGUID ) &&
1089  ( dmodel.wavelength == fmodel.wavelength ) &&
1090  ( dmodel.variance == fmodel.variance ) &&
1091  ( dmodel.meniscus == fmodel.meniscus ) &&
1092  ( dmodel.optics == fmodel.optics ) &&
1093  ( dmodel.analysis == fmodel.analysis ) &&
1094  ( dmodel.global == fmodel.global ) &&
1095  ( dmodel.coSedSolute == fmodel.coSedSolute ) &&
1096  ( dmodel.components.size() == fmodel.components.size() ) )
1097  { // Difference can only be in component values
1098  for ( int ii = 0; ii < dmodel.components.size(); ii++ )
1099  {
1100  if ( dmodel.components[ ii ] != fmodel.components[ ii ] )
1101  { // There is a components difference, so measure it
1102  US_Model::SimulationComponent* dcomp = &dmodel.components[ ii ];
1103  US_Model::SimulationComponent* fcomp = &fmodel.components[ ii ];
1104  double dcvalu = qAbs( dcomp->signal_concentration );
1105  double fcvalu = qAbs( fcomp->signal_concentration );
1106  double valmin = qMin( dcvalu, fcvalu );
1107  valmin = ( valmin == 0.0 ) ? dtoler : valmin;
1108  double difrat = qAbs( dcvalu - fcvalu ) / valmin;
1109  difmax = qMax( difrat, difmax );
1110 
1111  dcvalu = qAbs( dcomp->molar_concentration );
1112  fcvalu = qAbs( fcomp->molar_concentration );
1113  valmin = qMin( dcvalu, fcvalu );
1114  valmin = ( valmin == 0.0 ) ? dtoler : valmin;
1115  difrat = qAbs( dcvalu - fcvalu ) / valmin;
1116  difmax = qMax( difrat, difmax );
1117 
1118  dcvalu = qAbs( dcomp->vbar20 );
1119  fcvalu = qAbs( fcomp->vbar20 );
1120  valmin = qMin( dcvalu, fcvalu );
1121  valmin = ( valmin == 0.0 ) ? dtoler : valmin;
1122  difrat = qAbs( dcvalu - fcvalu ) / valmin;
1123  difmax = qMax( difrat, difmax );
1124 
1125  dcvalu = qAbs( dcomp->mw );
1126  fcvalu = qAbs( fcomp->mw );
1127  valmin = qMin( dcvalu, fcvalu );
1128  valmin = ( valmin == 0.0 ) ? dtoler : valmin;
1129  difrat = qAbs( dcvalu - fcvalu ) / valmin;
1130  difmax = qMax( difrat, difmax );
1131 
1132  dcvalu = qAbs( dcomp->s );
1133  fcvalu = qAbs( fcomp->s );
1134  valmin = qMin( dcvalu, fcvalu );
1135  valmin = ( valmin == 0.0 ) ? dtoler : valmin;
1136  difrat = qAbs( dcvalu - fcvalu ) / valmin;
1137  difmax = qMax( difrat, difmax );
1138 
1139  dcvalu = qAbs( dcomp->D );
1140  fcvalu = qAbs( fcomp->D );
1141  valmin = qMin( dcvalu, fcvalu );
1142  valmin = ( valmin == 0.0 ) ? dtoler : valmin;
1143  difrat = qAbs( dcvalu - fcvalu ) / valmin;
1144  difmax = qMax( difrat, difmax );
1145 DbgLv(1) << " CONFLICT? D dcv fcv dr" << dcvalu << fcvalu << difrat;
1146 
1147  dcvalu = qAbs( dcomp->f_f0 );
1148  fcvalu = qAbs( fcomp->f_f0 );
1149  valmin = qMin( dcvalu, fcvalu );
1150  valmin = ( valmin == 0.0 ) ? dtoler : valmin;
1151  difrat = qAbs( dcvalu - fcvalu ) / valmin;
1152  difmax = qMax( difrat, difmax );
1153  }
1154  }
1155 
1156  differs = ( difmax > dtoler );
1157 DbgLv(1) << " CONFLICT? model difmax differs" << difmax << differs;
1158  }
1159 
1160  }
1161 
1162  // if noise, check for differences beyond tolerance in noise values
1163  else if ( ddesc.recType == 4)
1164  {
1165  US_Noise dnoise;
1166  US_Noise fnoise;
1167  double difmax = 0.0;
1168 
1169  int dst = dnoise.load( QString::number( ddesc.recordID ), dbP );
1170  int fst = fnoise.load( ddesc.filename );
1171 
1172  if ( dst != US_DB2::OK )
1173  dnoise.description = "DBAD:" + dnoise.description;
1174 
1175  if ( fst != US_DB2::OK )
1176  fnoise.description = "FBAD:" + fnoise.description;
1177 
1178  if ( dnoise == fnoise )
1179  differs = false; // Differences only in formatting
1180 
1181  else if ( ( dnoise.type == fnoise.type ) &&
1182  ( dnoise.description == fnoise.description ) &&
1183  ( dnoise.noiseGUID == fnoise.noiseGUID ) &&
1184  ( dnoise.modelGUID == fnoise.modelGUID ) &&
1185  ( dnoise.minradius == fnoise.minradius ) &&
1186  ( dnoise.maxradius == fnoise.maxradius ) &&
1187  ( dnoise.values.size() == fnoise.values.size() ) )
1188  { // Difference can only be in noise values
1189  for ( int ii = 0; ii < dnoise.values.size(); ii++ )
1190  {
1191  double dnvalu = qAbs( dnoise.values[ ii ] );
1192  double fnvalu = qAbs( fnoise.values[ ii ] );
1193  double valmin = qMin( dnvalu, fnvalu );
1194  valmin = ( valmin == 0.0 ) ? dtoler : valmin;
1195  double difrat = qAbs( dnvalu - fnvalu ) / valmin;
1196  difmax = qMax( difrat, difmax );
1197  }
1198 
1199  differs = ( difmax > dtoler );
1200 DbgLv(1) << " CONFLICT? model difmax differs" << difmax << differs;
1201  }
1202  }
1203 
1204  return differs;
1205 }
1206