UltraScan III
us_solution_gui.cpp
Go to the documentation of this file.
1 
3 #include <QtGui>
4 
5 #include "us_settings.h"
6 #include "us_gui_settings.h"
7 #include "us_passwd.h"
8 #include "us_db2.h"
9 #include "us_investigator.h"
10 #include "us_buffer_gui.h"
11 #include "us_analyte_gui.h"
12 #include "us_solution.h"
13 #include "us_solution_gui.h"
14 
16  int expID,
17  int chID,
18  bool signal_wanted,
19  int select_db_disk,
20  const US_Solution& dataIn,
21  bool auto_save
22  ) : US_WidgetsDialog( 0, 0 ), experimentID( expID ), channelID( chID ),
23  signal( signal_wanted ), solution( dataIn ), autosave( auto_save )
24 {
25  setAttribute( Qt::WA_DeleteOnClose );
26 
28 
29  setWindowTitle( tr( "Solution Management" ) );
30  setPalette( US_GuiSettings::frameColor() );
31 
32  // Very light gray, for read-only line edits
33  QPalette gray = US_GuiSettings::editColor();
34  gray.setColor( QPalette::Base, QColor( 0xe0, 0xe0, 0xe0 ) );
35 
36  QGridLayout* main = new QGridLayout( this );
37  main->setSpacing ( 2 );
38  main->setContentsMargins ( 2, 2, 2, 2 );
39 
40  QFontMetrics fm( QFont( US_GuiSettings::fontFamily(),
42 
43  int row = 0;
44 
45  QStringList DB = US_Settings::defaultDB();
46  if ( DB.isEmpty() ) DB << "Undefined";
47  QLabel* lb_DB = us_banner( tr( "Database: " ) + DB.at( 0 ) );
48  lb_DB->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
49  main->addWidget( lb_DB, row++, 0, 1, 3 );
50 
51  // First column
52  if ( US_Settings::us_inv_level() > 2 )
53  {
54  QPushButton* pb_investigator = us_pushbutton( tr( "Select Investigator" ) );
55  connect( pb_investigator, SIGNAL( clicked() ), SLOT( sel_investigator() ) );
56  main->addWidget( pb_investigator, row++, 0 );
57  }
58  else
59  {
60  QLabel* lb_investigator = us_label( tr( "Investigator:" ) );
61  main->addWidget( lb_investigator, row++, 0 );
62  }
63 
64  // Available solutions
65  QLabel* lb_banner2 = us_banner( tr( "Click on solution to select" ), -2 );
66  lb_banner2->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
67  lb_banner2->setMinimumWidth( 400 );
68  main->addWidget( lb_banner2, row++, 0 );
69 
71  lw_solutions-> setSortingEnabled( true );
72  connect( lw_solutions, SIGNAL( itemClicked ( QListWidgetItem* ) ),
73  SLOT ( selectSolution ( QListWidgetItem* ) ) );
74  main->addWidget( lw_solutions, row, 0, 7, 1 );
75 
76  row += 7;
77 
78  QHBoxLayout* lo_amount = new QHBoxLayout();
79 
80  lb_amount = us_label( tr( "Analyte Molar Ratio:" ) );
81  lo_amount->addWidget( lb_amount );
82 
83  ct_amount = us_counter ( 2, 0, 100, 1 ); // #buttons, low, high, start_value
84  ct_amount->setStep( 1 );
85  ct_amount->setFont( QFont( US_GuiSettings::fontFamily(),
87  lo_amount->addWidget( ct_amount );
88  main->addLayout( lo_amount, row++, 0 );
89 
90  QLabel* lb_banner3 = us_banner( tr( "Current solution contents" ), -2 );
91  lb_banner3->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
92  main->addWidget( lb_banner3, row++, 0 );
93 
95  lw_analytes-> setSortingEnabled( true );
96  connect( lw_analytes, SIGNAL( itemClicked ( QListWidgetItem* ) ),
97  SLOT ( selectAnalyte( QListWidgetItem* ) ) );
98  connect( lw_analytes, SIGNAL( itemDoubleClicked ( QListWidgetItem* ) ),
99  SLOT ( changeAnalyte ( QListWidgetItem* ) ) );
100 
101  int add_rows = ( US_Settings::us_debug() == 0 ) ? 6 : 8;
102 
103  main->addWidget( lw_analytes, row, 0, add_rows, 1 );
104 
105  row += add_rows;
106 
107  // Second column
108  row = 1;
109 
110  le_investigator = us_lineedit( tr( "Not Selected" ) );
111  le_investigator->setReadOnly( true );
112  main->addWidget( le_investigator, row++, 1, 1, 2 );
113 
114  disk_controls = new US_Disk_DB_Controls( select_db_disk );
115  connect( disk_controls, SIGNAL( changed ( bool ) ),
116  SLOT ( source_changed( bool ) ) );
117  main->addLayout( disk_controls, row++, 1, 1, 2 );
118 
119  pb_query = us_pushbutton( tr( "Query Solutions" ), true );
120  connect( pb_query, SIGNAL( clicked() ), SLOT( load() ) );
121  main->addWidget( pb_query, row, 1 );
122 
123  pb_save = us_pushbutton( tr( "Save Solution" ), false );
124  connect( pb_save, SIGNAL( clicked() ), SLOT( save() ) );
125  main->addWidget( pb_save, row++, 2 );
126 
127  pb_addAnalyte = us_pushbutton( tr( "Add Analyte" ), true );
128  connect( pb_addAnalyte, SIGNAL( clicked() ), SLOT( addAnalyte() ) );
129  main->addWidget( pb_addAnalyte, row, 1 );
130 
131  pb_del = us_pushbutton( tr( "Delete Solution" ), false );
132  connect( pb_del, SIGNAL( clicked() ), SLOT( delete_solution() ) );
133  main->addWidget( pb_del, row++, 2 );
134 
135  pb_removeAnalyte = us_pushbutton( tr( "Remove Analyte" ), false );
136  connect( pb_removeAnalyte, SIGNAL( clicked() ), SLOT( removeAnalyte() ) );
137  main->addWidget( pb_removeAnalyte, row, 1 );
138 
139  pb_buffer = us_pushbutton( tr( "Select Buffer" ), true );
140  connect( pb_buffer, SIGNAL( clicked() ), SLOT( selectBuffer() ) );
141  main->addWidget( pb_buffer, row++, 2 );
142 
143  le_bufferInfo = us_lineedit( "", 1 );
144  le_bufferInfo ->setPalette ( gray );
145  le_bufferInfo ->setReadOnly( true );
146  main->addWidget( le_bufferInfo, row++, 1, 1, 2 );
147 
148  QLabel* lb_banner4 = us_banner( tr( "Edit solution properties" ), -2 );
149  lb_banner4->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
150  main->addWidget( lb_banner4, row++, 1, 1, 2 );
151 
152  QLabel* lb_solutionDesc = us_label( tr( "Solution Name:" ) );
153  main->addWidget( lb_solutionDesc, row++, 1, 1, 2 );
154 
155  le_solutionDesc = us_lineedit( "", 1 );
156  connect( le_solutionDesc, SIGNAL( editingFinished () ),
157  SLOT ( saveDescription () ) );
158  main->addWidget( le_solutionDesc, row++, 1, 1, 2 );
159 
160  QLabel* lb_commonVbar20 = us_label( tr( "Common VBar (20C):" ) );
161  main->addWidget( lb_commonVbar20, row, 1 );
162 
163  le_commonVbar20 = us_lineedit( "", 1 );
164  le_commonVbar20->setPalette ( gray );
165  le_commonVbar20->setReadOnly( true );
166  main->addWidget( le_commonVbar20, row++, 2 );
167 
168  QLabel* lb_density = us_label( tr( "Buffer density:" ) );
169  main->addWidget( lb_density, row, 1 );
170 
171  le_density = us_lineedit( "", 1 );
172  le_density->setPalette ( gray );
173  le_density->setReadOnly( true );
174  main->addWidget( le_density, row++, 2 );
175 
176  QLabel* lb_viscosity = us_label( tr( "Buffer viscosity:" ) );
177  main->addWidget( lb_viscosity, row, 1 );
178 
179  le_viscosity = us_lineedit( "", 1 );
180  le_viscosity->setPalette ( gray );
181  le_viscosity->setReadOnly( true );
182  main->addWidget( le_viscosity, row++, 2 );
183 
184  QLabel* lb_storageTemp = us_label( tr( "Storage Temperature:" ) );
185  main->addWidget( lb_storageTemp, row, 1 );
186 
187  le_storageTemp = us_lineedit( "", 1 );
188  connect( le_storageTemp, SIGNAL( textEdited ( const QString& ) ),
189  SLOT ( saveTemperature ( const QString& ) ) );
190  main->addWidget( le_storageTemp, row++, 2 );
191 
192  QLabel* lb_notes = us_label( tr( "Solution notes:" ) );
193  main->addWidget( lb_notes, row++, 1, 1, 2 );
194 
195  te_notes = us_textedit();
196  connect( te_notes, SIGNAL( textChanged( void ) ),
197  SLOT ( saveNotes ( void ) ) );
198  main->addWidget( te_notes, row, 1, 5, 2 );
199  te_notes->setMinimumHeight( 200 );
200  te_notes->setReadOnly( false );
201  row += 3;
202 
203  QLabel* lb_guid = us_label( tr( "Global Identifier:" ) );
204  main->addWidget( lb_guid, row++, 1, 1, 2 );
205 
206  le_guid = us_lineedit( "" );
207  le_guid->setPalette ( gray );
208  le_guid->setReadOnly( true );
209  main->addWidget( le_guid, row++, 1, 1, 2 );
210 
211  if ( US_Settings::us_debug() == 0 )
212  {
213  lb_guid->setVisible( false );
214  le_guid->setVisible( false );
215  }
216 
217  // Some pushbuttons
218  QHBoxLayout* buttons = new QHBoxLayout;
219 
220  QPushButton* pb_reset = us_pushbutton( tr( "Reset" ) );
221  connect( pb_reset, SIGNAL( clicked() ), SLOT( newSolution() ) );
222  buttons->addWidget( pb_reset );
223 
224  QPushButton* pb_help = us_pushbutton( tr( "Help" ) );
225  connect( pb_help, SIGNAL( clicked() ), SLOT( help() ) );
226  buttons->addWidget( pb_help );
227 
228  pb_accept = us_pushbutton( tr( "Close" ) );
229 
230  if ( signal )
231  {
232  QPushButton* pb_cancel = us_pushbutton( tr( "Cancel" ) );
233  connect( pb_cancel, SIGNAL( clicked() ), SLOT( cancel() ) );
234  buttons->addWidget( pb_cancel );
235 
236  pb_accept -> setText( tr( "Accept" ) );
237  }
238 
239  connect( pb_accept, SIGNAL( clicked() ), SLOT( accept() ) );
240  buttons->addWidget( pb_accept );
241 
242  // Now let's assemble the page
243 
244  main->addLayout( buttons, row, 0, 1, 3 );
245 
246  reset();
247 
248  // Load the solution descriptions
249  load();
250 
251  changed = false;
252 
253  // Select the current one if we know what it is
254  if ( solution.solutionID > 0 )
255  {
256  QList< QListWidgetItem* > items
257  = lw_solutions->findItems( solution.solutionDesc, Qt::MatchExactly );
258 
259  // should be exactly 1, but let's make sure
260  if ( items.size() == 1 )
261  {
262  selectSolution( items[ 0 ] );
263  lw_solutions->setCurrentItem( items[ 0 ] );
264  }
265  }
266 }
267 
268 // Function to refresh the display with values from the solution structure,
269 // and to enable/disable features
271 {
272  QList< US_Solution::AnalyteInfo >& ai = solution.analyteInfo;
273  QString bufferDesc = solution.buffer.description;
274 
276  le_solutionDesc -> setText( solution.solutionDesc );
277  le_commonVbar20 -> setText( QString::number( solution.commonVbar20 ) );
278  le_density -> setText( QString::number( solution.buffer.density ) );
279  le_viscosity -> setText( QString::number( solution.buffer.viscosity ) );
280  le_storageTemp -> setText( QString::number( solution.storageTemp ) );
281  te_notes -> setText( solution.notes );
282  le_guid -> setText( solution.solutionGUID );
283  ct_amount -> disconnect();
284  ct_amount -> setEnabled( false );
285  ct_amount -> setValue( 1 );
286 
287  pb_buffer -> setEnabled( true );
288 
289  pb_addAnalyte -> setEnabled( true );
290  pb_removeAnalyte-> setEnabled( false );
291 
292  // Let's calculate if we're eligible to save this solution
293  pb_save -> setEnabled( false );
294  if ( ! bufferDesc.isEmpty() ) //we can have a solution with buffer only
295  {
296  pb_save -> setEnabled( true );
297  }
298 
299  // We can always delete something, even if it's just what's in the dialog
300  pb_del -> setEnabled( false );
301  if ( lw_solutions->currentRow() != -1 )
302  {
303  pb_del -> setEnabled( true );
304  }
305 
306  // Display analytes that have been selected
307  lw_analytes->clear();
308  analyteMap.clear();
309  for ( int i = 0; i < ai.size(); i++ )
310  {
311  // Create a map to account for sorting of the list
312  QListWidgetItem* item = new QListWidgetItem( ai[ i ].analyte.description, lw_analytes );
313  analyteMap[ item ] = i;
314 
315  lw_analytes->addItem( item );
316  }
317 
318  // Turn the red label back
319  QPalette p = lb_amount->palette();
320  p.setColor( QPalette::WindowText, Qt::white );
321  lb_amount->setPalette( p );
322 
323  // Figure out if the accept button should be enabled
324  if ( ! signal ) // Then it's just a close button
325  pb_accept->setEnabled( true );
326 
327  else if ( solution.saveStatus == US_Solution::BOTH )
328  pb_accept->setEnabled( true );
329 
330  else if ( ( ! disk_controls->db() ) && solution.saveStatus == US_Solution::HD_ONLY )
331  pb_accept->setEnabled( true );
332 
333  else if ( ( disk_controls->db() ) && solution.saveStatus == US_Solution::DB_ONLY )
334  pb_accept->setEnabled( true );
335 
336  else
337  pb_accept->setEnabled( false );
338 
339  // Display investigator
341 
342  QString number = ( investigatorID > 0 )
343  ? QString::number( investigatorID ) + ": "
344  : "";
345 
346  le_investigator->setText( number + US_Settings::us_inv_name() );
347 }
348 
349 // Function to accept the current solution and return
351 {
352  if ( changed )
353  {
354  bool save_it = autosave;
355 
356  if ( ! autosave )
357  {
358  int response = QMessageBox::question( this,
359  tr( "Save Changed Solution?" ),
360  tr( "Changes were made to the solution and you did not save them.\n"
361  "Do you wish to save the solution now?" ),
362  QMessageBox::Yes, QMessageBox::No );
363 
364  save_it = ( response == QMessageBox::Yes );
365  }
366 
367  if ( save_it )
368  {
369  save( false ); // make sure the current selections have been saved
370  }
371  }
372 
373  if ( signal )
374  {
375  if ( le_solutionDesc->text().isEmpty() )
376  {
377  QMessageBox::information( this,
378  tr( "Attention" ),
379  tr( "Please enter a description for\n"
380  "your solution before accepting." ) );
381  return;
382  }
383 
384  if ( le_storageTemp->text().isEmpty() )
385  solution.storageTemp = 0;
386 
388  }
389 
390  close();
391 }
392 
393 // Function to cancel the current dialog and return
395 {
396  if ( signal )
398 
399  close();
400 }
401 
402 // Function to select the current investigator
404 {
405  US_Investigator* inv_dialog = new US_Investigator( true, investigatorID );
406 
407  connect( inv_dialog,
408  SIGNAL( investigator_accepted( int ) ),
409  SLOT ( assign_investigator ( int ) ) );
410 
411  inv_dialog->exec();
412 }
413 
414 // Function to assign the selected investigator as current
416 {
417  investigatorID = invID;
418 
419  QString number = ( investigatorID > 0 )
420  ? QString::number( investigatorID ) + ": "
421  : "";
422 
423  le_investigator->setText( number + US_Settings::us_inv_name() );
424 }
425 
426 // Function to load solutions into solutions list widget
428 {
429  if ( disk_controls->db() )
430  loadDB();
431 
432  else
433  loadDisk();
434 
435 }
436 
437 // Function to load solutions from disk
439 {
440  QString path;
441  if ( ! solution.diskPath( path ) ) return;
442 
443  IDs.clear();
444  descriptions.clear();
445  GUIDs.clear();
446  filenames.clear();
447 
448  QDir dir( path );
449  QStringList filter( "S*.xml" );
450  QStringList names = dir.entryList( filter, QDir::Files, QDir::Name );
451 
452  QFile a_file;
453 
454  for ( int i = 0; i < names.size(); i++ )
455  {
456  a_file.setFileName( path + "/" + names[ i ] );
457 
458  if ( ! a_file.open( QIODevice::ReadOnly | QIODevice::Text) ) continue;
459 
460  QXmlStreamReader xml( &a_file );
461 
462  while ( ! xml.atEnd() )
463  {
464  xml.readNext();
465 
466  if ( xml.isStartElement() )
467  {
468  if ( xml.name() == "solution" )
469  {
470  QXmlStreamAttributes a = xml.attributes();
471 
472  IDs << a.value( "id" ).toString();
473  GUIDs << a.value( "guid" ).toString();
474  filenames << path + "/" + names[ i ];
475 
476  }
477 
478  else if ( xml.name() == "description" )
479  {
480  xml.readNext();
481  descriptions << xml.text().toString();
482  }
483  }
484  }
485 
486  a_file.close();
487  }
488 
489  loadSolutions();
490 }
491 
492 // Function to load solutions from db
494 {
495  US_Passwd pw;
496  QString masterPW = pw.getPasswd();
497  US_DB2 db( masterPW );
498 
499  if ( db.lastErrno() != US_DB2::OK )
500  {
501  db_error( db.lastError() );
502  return;
503  }
504 
506 
507  QStringList q( "all_solutionIDs" );
508  q << QString::number( investigatorID );
509  db.query( q );
510 
511  if ( db.lastErrno() != US_DB2::OK ) return;
512 
513  IDs.clear();
514  descriptions.clear();
515  GUIDs.clear();
516  filenames.clear();
517 
518  while ( db.next() )
519  {
520  QString newID = db.value( 0 ).toString();
521  IDs << newID;
522  descriptions << db.value( 1 ).toString();
523  GUIDs << QString( "" );
524  filenames << QString( "" );
525  }
526 
527  loadSolutions();
528 }
529 
530 // Function to load the solutions list widget from the solutions data structure
532 {
533  lw_solutions->clear();
534  info.clear();
535  solutionMap.clear();
536 
537  for ( int i = 0; i < descriptions.size(); i++ )
538  {
539  SolutionInfo si;
540  si.solutionID = IDs [ i ].toInt();
541  si.description = descriptions[ i ];
542  si.GUID = GUIDs [ i ];
543  si.filename = filenames [ i ];
544  si.index = i;
545  info << si;
546 
547  // Create a map to account for automatic sorting of the list
548  QListWidgetItem* item = new QListWidgetItem( descriptions[ i ], lw_solutions );
549  solutionMap[ item ] = i;
550 
551  lw_solutions->addItem( item );
552  }
553 
554 }
555 
556 // Function to handle when analyte listwidget item is selected
557 void US_SolutionGui::selectSolution( QListWidgetItem* item )
558 {
559  // Account for the fact that the list has been sorted
560  int ndx = solutionMap[ item ];
561  int solutionID = info[ ndx ].solutionID;
562  QString solutionGUID = info[ ndx ].GUID;
563 
564  solution.clear();
565 
566  int status = US_DB2::OK;
567 
568  if ( disk_controls->db() )
569  {
570  US_Passwd pw;
571  QString masterPW = pw.getPasswd();
572  US_DB2 db( masterPW );
573 
574  if ( db.lastErrno() != US_DB2::OK )
575  {
576  db_error( db.lastError() );
577  return;
578  }
579 
580  status = solution.readFromDB ( solutionID, &db );
581 
582  // Error reporting
583  if ( status == US_DB2::NO_BUFFER )
584  {
585  QMessageBox::information( this,
586  tr( "Attention" ),
587  tr( "The buffer this solution refers to was not found.\n"
588  "Please restore and try again.\n" ) );
589  }
590 
591  else if ( status == US_DB2::NO_ANALYTE )
592  {
593  QMessageBox::information( this,
594  tr( "Attention" ),
595  tr( "One of the analytes this solution refers to was not found.\n"
596  "Please restore and try again.\n" ) );
597  }
598 
599  else if ( status != US_DB2::OK )
600  db_error( db.lastError() );
601 
602  }
603 
604  else
605  {
606  status = solution.readFromDisk( solutionGUID );
607 
608  // Error reporting
609  if ( status == US_DB2::NO_BUFFER )
610  {
611  QMessageBox::information( this,
612  tr( "Attention" ),
613  tr( "The buffer this solution refers to was not found.\n"
614  "Please restore and try again.\n" ) );
615  }
616 
617  else if ( status == US_DB2::NO_ANALYTE )
618  {
619  QMessageBox::information( this,
620  tr( "Attention" ),
621  tr( "One of the analytes this solution refers to was not found.\n"
622  "Please restore and try again.\n" ) );
623  }
624 
625  else if ( status != US_DB2::OK )
626  {
627  QMessageBox::information( this,
628  tr( "Disk Read Problem" ),
629  tr( "Could not read data from the disk.\n"
630  "Disk status: " ) + QString::number( status ) );
631  }
632  }
633 
634  reset();
635  changed = false;
636 }
637 
638 // Function to add analyte to solution
640 {
641  int dbdisk = ( disk_controls->db() ) ? US_Disk_DB_Controls::DB
643 
644  US_AnalyteGui* analyte_dialog = new US_AnalyteGui( true, QString(), dbdisk );
645 
646  connect( analyte_dialog, SIGNAL( valueChanged ( US_Analyte ) ),
647  this, SLOT ( assignAnalyte ( US_Analyte ) ) );
648 
649  connect( analyte_dialog, SIGNAL( use_db ( bool ) ),
650  SLOT ( update_disk_db( bool ) ) );
651 
652  analyte_dialog->exec();
653  qApp->processEvents();
654  changed = true;
655 
656 }
657 
658 // Get information about selected analyte
660 {
661  US_Solution::AnalyteInfo newInfo;
662  newInfo.analyte = data;
663  newInfo.amount = 1;
664 
665  if ( disk_controls->db() )
666  {
667  // Now double-check analyteID from db if we can
668  US_Passwd pw;
669  QString masterPW = pw.getPasswd();
670  US_DB2 db( masterPW );
671 
672  if ( db.lastErrno() == US_DB2::OK )
673  {
674  QStringList q( "get_analyteID" );
675  q << newInfo.analyte.analyteGUID;
676  db.query( q );
677 
678  if ( db.next() )
679  newInfo.analyte.analyteID = db.value( 0 ).toString();
680  }
681  }
682 
683  // Make sure item has not been added already
684  // Check manually because the amounts could be different, but it's still
685  // the same analyte
686  bool found = false;
687  foreach ( US_Solution::AnalyteInfo itemInfo, solution.analyteInfo )
688  {
689  if ( itemInfo.analyte == newInfo.analyte )
690  {
691  found = true;
692  break;
693  }
694  }
695 
696  if ( found )
697  {
698  QMessageBox::information( this,
699  tr( "Attention" ),
700  tr( "Your solution already contains this analyte\n"
701  "If you wish to change the amount, remove it and "
702  "add it again.\n" ) );
703  return;
704  }
705 
706  solution.analyteInfo << newInfo;
707 
709 
710  // We're maintaining a map to account for automatic sorting of the list
711  QListWidgetItem* item = new QListWidgetItem( newInfo.analyte.description, lw_analytes );
712  analyteMap[ item ] = solution.analyteInfo.size() - 1; // The one we just added
713 
714  reset();
715  changed = true;
716 }
717 
718 // Function to handle when solution listwidget item is selected
719 void US_SolutionGui::selectAnalyte( QListWidgetItem* item )
720 {
721  // Get the right index in the sorted list, and load the amount
722  int ndx = analyteMap[ item ];
723  ct_amount ->setValue( solution.analyteInfo[ ndx ].amount );
724 
725  // Now turn the label red to catch attention
726  QPalette p = lb_amount->palette();
727  p.setColor( QPalette::WindowText, Qt::red );
728  lb_amount->setPalette( p );
729 
730  pb_removeAnalyte ->setEnabled( true );
731  ct_amount ->setEnabled( true );
732  connect( ct_amount, SIGNAL( valueChanged ( double ) ), // if the user has changed it
733  SLOT ( saveAmount ( double ) ) );
734  changed = true;
735 }
736 
737 // Function to handle when solution listwidget item is double-clicked
738 void US_SolutionGui::changeAnalyte( QListWidgetItem* item )
739 {
740  // Get the right index in the sorted list, and get the analyte info
741  int ndx = analyteMap[ item ];
742  US_Analyte currentAnalyte = solution.analyteInfo[ ndx].analyte;
743 
744  int dbdisk = ( disk_controls->db() ) ? US_Disk_DB_Controls::DB
746 
747  US_AnalyteGui* analyte_dialog = new US_AnalyteGui( true,
748  currentAnalyte.analyteGUID,
749  dbdisk );
750 
751  connect( analyte_dialog, SIGNAL( valueChanged ( US_Analyte ) ),
752  this, SLOT ( replaceAnalyte ( US_Analyte ) ) );
753 
754  connect( analyte_dialog, SIGNAL( use_db ( bool ) ),
755  SLOT ( update_disk_db( bool ) ) );
756 
757  analyte_dialog->exec();
758  qApp->processEvents();
759  changed = true;
760 }
761 
762 // Replace current analyte item with information about selected analyte
764 {
765  US_Solution::AnalyteInfo newInfo;
766  newInfo.analyte = data;
767  newInfo.amount = 1;
768 
769  if ( disk_controls->db() )
770  {
771  // Now double-check analyteID from db if we can
772  US_Passwd pw;
773  QString masterPW = pw.getPasswd();
774  US_DB2 db( masterPW );
775 
776  if ( db.lastErrno() == US_DB2::OK )
777  {
778  QStringList q( "get_analyteID" );
779  q << newInfo.analyte.analyteGUID;
780  db.query( q );
781 
782  if ( db.next() )
783  newInfo.analyte.analyteID = db.value( 0 ).toString();
784  }
785  }
786 
787  // Which item is selected currently?
788  QListWidgetItem* item = lw_analytes->currentItem();
789  int ndx = analyteMap[ item ];
790 
791  // Let's see if the item has been added already, checking manually
792  // because the amounts could be different but it's still
793  // the same analyte
794  bool found = false;
795  for ( int i = 0; i < solution.analyteInfo.size(); i++ )
796  {
797  // if i == ndx, this is the one we're going to delete in a minute
799  if ( ( itemInfo.analyte == newInfo.analyte ) &&
800  ( i != ndx ) )
801  {
802  found = true;
803  break;
804  }
805  }
806 
807  if ( found )
808  {
809  QMessageBox::information( this,
810  tr( "Attention" ),
811  tr( "Your solution already contains this analyte\n"
812  "If you wish to change the amount, remove it and "
813  "add it again.\n" ) );
814  return;
815  }
816 
817  // Delete the old one and add the new one
818  solution.analyteInfo.removeAt( ndx );
819  lw_analytes->removeItemWidget( item );
820  solution.analyteInfo << newInfo;
821 
823 
824  // We're maintaining a map to account for automatic sorting of the list
825  QListWidgetItem* newItem = new QListWidgetItem( newInfo.analyte.description,
826  lw_analytes );
827  analyteMap[ newItem ] = solution.analyteInfo.size() - 1; // The one we just added
828  ndx = analyteMap[ newItem ];
829 
830  reset();
831  changed = true;
832 
833  // Find the item we just added and select it
834  QList< QListWidgetItem* > items = lw_analytes->findItems( newInfo.analyte.description,
835  Qt::MatchExactly );
836  lw_analytes->setCurrentItem( items[ 0 ] ); // should be only one
837  selectAnalyte( items[ 0 ] );
838 }
839 
840 // Function to add analyte to solution
842 {
843  // Allow for the fact that this listwidget is sorted
844  QListWidgetItem* item = lw_analytes->currentItem();
845  int ndx = analyteMap[ item ];
846 
847  solution.analyteInfo.removeAt( ndx );
848  lw_analytes ->removeItemWidget( item );
849 
851 
852  reset();
853  changed = true;
854 }
855 
856 // Function to calculate the default commonVbar20 value
858 {
859  solution.commonVbar20 = 0.0;
860 
861  if ( solution.analyteInfo.size() == 1 )
862  solution.commonVbar20 = solution.analyteInfo[ 0 ].analyte.vbar20;
863 
864  else // multiple analytes
865  {
866  double numerator = 0.0;
867  double denominator = 0.0;
869  {
870  numerator += ai.analyte.vbar20 * ai.analyte.mw * ai.amount;
871  denominator += ai.analyte.mw * ai.amount;
872  }
873 
874  solution.commonVbar20 = ( denominator == 0 ) ? 0.0 : ( numerator / denominator );
875 
876  }
877  changed = true;
878 
879 }
880 
881 // Create a dialog to request a buffer selection
883 {
884  int dbdisk = ( disk_controls->db() ) ? US_Disk_DB_Controls::DB
886 
887  US_BufferGui* buffer_dialog = new US_BufferGui( true,
888  solution.buffer, dbdisk );
889 
890  connect( buffer_dialog, SIGNAL( valueChanged ( US_Buffer ) ),
891  this, SLOT ( assignBuffer ( US_Buffer ) ) );
892 
893  connect( buffer_dialog, SIGNAL( use_db ( bool ) ),
894  SLOT ( update_disk_db( bool ) ) );
895 
896  buffer_dialog->exec();
897  qApp->processEvents();
898  changed = true;
899 }
900 
901 // Get information about selected buffer
903 {
904  if ( disk_controls->db() )
905  {
906  // Now get the corresponding bufferID, if we can
907  US_Passwd pw;
908  QString masterPW = pw.getPasswd();
909  US_DB2 db( masterPW );
910 
911  if ( db.lastErrno() == US_DB2::OK )
912  {
913  QStringList q( "get_bufferID" );
914  q << newBuffer.GUID;
915  db.query( q );
916 
917  if ( db.next() )
918  newBuffer.bufferID = db.value( 0 ).toString();
919 
920  else
921  newBuffer.bufferID = QString( "-1" );
922 
923  }
924  }
925 
926  solution.buffer = newBuffer;
927 
928  reset();
929  changed = true;
930 }
931 
932 // Function to update the amount that is associated with an individual analyte
933 void US_SolutionGui::saveAmount( double amount )
934 {
935  // Get the right index in the sorted list of analytes
936  QListWidgetItem* item = lw_analytes->currentItem();
937 
938  // if item not selected return
939 
940  int ndx = analyteMap[ item ];
941  solution.analyteInfo[ ndx ].amount = amount;
942 
944 
945  // Update commonVbar20 value in GUI
946  le_commonVbar20 -> setText( QString::number( solution.commonVbar20 ) );
947  changed = true;
948 }
949 
950 // Function to update the description associated with the current solution
952 {
954  changed = true;
955  QListWidgetItem* item = new QListWidgetItem( solution.solutionDesc );
956 
957  if ( ! solutionMap.contains( item ) )
958  {
959  solution.solutionGUID.clear();
960  reset();
961  }
962 }
963 
964 // Function to update the storage temperature associated with the current solution
965 void US_SolutionGui::saveTemperature( const QString& )
966 {
967  solution.storageTemp = le_storageTemp ->text().toDouble();
968  changed = true;
969 }
970 
971 // Function to update the notes associated with the current solution
973 {
974  // Let's see if the notes have actually changed
975  if ( solution.notes != te_notes->toPlainText() )
976  {
977  solution.notes = te_notes ->toPlainText();
978  }
979  changed = true;
980 }
981 
982 // Function to create a new solution
984 {
985  IDs.clear();
986  descriptions.clear();
987  GUIDs.clear();
988  filenames.clear();
989 
990  analyteMap.clear();
991  solutionMap.clear();
992 
993  info.clear();
994  solution.clear();
995 
996  lw_solutions->clear();
997  reset();
998  changed = true;
999 }
1000 
1001 // Function to save solution information to disk or db
1002 void US_SolutionGui::save( bool display_status )
1003 {
1004  if ( le_solutionDesc->text().isEmpty() )
1005  {
1006  QMessageBox::information( this,
1007  tr( "Attention" ),
1008  tr( "Please enter a description for\n"
1009  "your solution before saving it!" ) );
1010  return;
1011  }
1012 
1013  if ( le_storageTemp->text().isEmpty() )
1014  solution.storageTemp = 0;
1015 
1016  if ( disk_controls->db() )
1017  {
1018  US_Passwd pw;
1019  QString masterPW = pw.getPasswd();
1020  US_DB2 db( masterPW );
1021 
1022  if ( db.lastErrno() != US_DB2::OK )
1023  {
1024  db_error( db.lastError() );
1025  return;
1026  }
1027 
1028  int status = solution.saveToDB( experimentID, channelID, &db );
1029 
1030  if ( status != US_DB2::OK && ! display_status ) // then we return but no status msg
1031  return;
1032 
1033  else if ( status == US_DB2::NO_BUFFER )
1034  {
1035  QMessageBox::information( this,
1036  tr( "Attention" ),
1037  tr( "There was a problem saving the buffer to the database.\n" ) );
1038  return;
1039  }
1040 
1041  else if ( status == US_DB2::NOROWS )
1042  {
1043  QMessageBox::information( this,
1044  tr( "Attention" ) ,
1045  tr( "A solution component is missing from the database, "
1046  "and the attempt to save it failed.\n") );
1047  return;
1048  }
1049 
1050  else if ( status != US_DB2::OK )
1051  {
1052  QMessageBox::information( this,
1053  tr( "Attention" ) ,
1054  db.lastError() );
1055  return;
1056  }
1057 
1058  }
1059 
1060  else
1061  solution.saveToDisk();
1062 
1063  if ( display_status )
1064  {
1065  QMessageBox::information( this,
1066  tr( "Save results" ),
1067  tr( "Solution saved" ) );
1068  }
1069 
1070  // Refresh solution list
1071  solutionMap.clear();
1072  lw_solutions->clear();
1073 
1074  load();
1075  reset();
1076 
1077  // Select the solution
1078  QList< QListWidgetItem* > items
1079  = lw_solutions->findItems( solution.solutionDesc, Qt::MatchExactly );
1080 
1081  // should be exactly 1, but let's make sure
1082  if ( items.size() == 1 )
1083  {
1084  selectSolution( items[ 0 ] );
1085  lw_solutions->setCurrentItem( items[ 0 ] );
1086  }
1087 
1088  changed = false;
1089 }
1090 
1091 // Function to delete a solution from disk, db, or in the current form
1093 {
1094  int status = US_DB2::OK;
1095 
1096  if ( disk_controls->db() )
1097  {
1098  US_Passwd pw;
1099  QString masterPW = pw.getPasswd();
1100  US_DB2 db( masterPW );
1101 
1102  if ( db.lastErrno() != US_DB2::OK )
1103  {
1104  db_error( db.lastError() );
1105  return;
1106  }
1107 
1108  status = solution.deleteFromDB( &db );
1109  }
1110 
1111  else
1112  status = solution.deleteFromDisk();
1113 
1114  if ( status == US_DB2::SOLUT_IN_USE )
1115  {
1116  QMessageBox::warning( this,
1117  tr( "Delete aborted" ),
1118  tr( "Solution NOT Deleted, since it is in use\n"
1119  "by one or more experiments" ) );
1120  return;
1121  }
1122 
1123  solution.clear();
1124  analyteMap.clear();
1125  load();
1126  reset();
1127 
1128  QMessageBox::information( this,
1129  tr( "Delete results" ),
1130  tr( "Solution Deleted" ) );
1131  changed = true;
1132 }
1133 
1135 {
1136  QStringList DB = US_Settings::defaultDB();
1137 
1138  if ( db && ( DB.size() < 5 ) )
1139  {
1140  QMessageBox::warning( this,
1141  tr( "Attention" ),
1142  tr( "There is no default database set." ) );
1143  }
1144 
1145  emit use_db( db );
1146  qApp->processEvents();
1147 
1148  // Clear out solution list
1149  solutionMap.clear();
1150  lw_solutions->clear();
1151 
1152  load();
1153  reset();
1154 }
1155 
1157 {
1158  ( db ) ? disk_controls->set_db() : disk_controls->set_disk();
1159 
1160  // Pass it on
1161  emit use_db( db );
1162 }
1163 
1164 // Function to display an error returned from the database
1165 void US_SolutionGui::db_error( const QString& error )
1166 {
1167  QMessageBox::warning( this, tr( "Database Problem" ),
1168  tr( "Database returned the following error: \n" ) + error );
1169 }