Update Turkish translation
[dasher.git] / Src / DasherCore / UserLogTrial.cpp
blobb1540e35458147bb6b05407376983126c2bab4a3
1 #include "../Common/Common.h"
3 #include <cstring>
4 #include "UserLogTrial.h"
6 // Track memory leaks on Windows to the line that new'd the memory
7 #ifdef _WIN32
8 #ifdef _DEBUG_MEMLEAKS
9 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
10 #define new DEBUG_NEW
11 #undef THIS_FILE
12 static char THIS_FILE[] = __FILE__;
13 #endif
14 #endif
16 CUserLogTrial::CUserLogTrial(const string& strCurrentTrialFilename)
18 //CFunctionLogger f1("CUserLogTrial::CUserLogTrial", g_pLogger);
20 InitMemberVars();
22 m_strCurrentTrialFilename = strCurrentTrialFilename;
25 CUserLogTrial::~CUserLogTrial()
27 //CFunctionLogger f1("CUserLogTrial::~CUserLogTrial", g_pLogger);
29 for (unsigned int i = 0; i < m_vpParams.size(); i++)
31 CUserLogParam* pParam = (CUserLogParam*) m_vpParams[i];
33 if (pParam != NULL)
35 delete pParam;
36 pParam = NULL;
40 for (unsigned int i = 0; i < m_vpNavCycles.size(); i++)
42 NavCycle* pCycle = (NavCycle*) m_vpNavCycles[i];
44 if (pCycle != NULL)
46 if (pCycle->pSpan != NULL)
48 delete pCycle->pSpan;
49 pCycle->pSpan = NULL;
52 for (unsigned int i = 0; i < pCycle->vectorButtons.size(); i++)
54 CUserButton* pButton = (CUserButton*) pCycle->vectorButtons[i];
56 if (pButton != NULL)
58 delete pButton;
59 pButton = NULL;
63 for (unsigned int i = 0; i < pCycle->vectorMouseLocations.size(); i++)
65 CUserLocation* pLocation = (CUserLocation*) pCycle->vectorMouseLocations[i];
67 if (pLocation != NULL)
69 delete pLocation;
70 pLocation = NULL;
74 for (unsigned int i = 0; i < pCycle->vectorNavLocations.size(); i++)
76 NavLocation* pLocation = (NavLocation*) pCycle->vectorNavLocations[i];
77 if (pLocation != NULL)
79 CTimeSpan* pSpan = pLocation->span;
81 if (pSpan != NULL)
83 delete pSpan;
84 pSpan = NULL;
87 Dasher::VECTOR_SYMBOL_PROB* pVectorAdded = pLocation->pVectorAdded;
88 if (pVectorAdded != NULL)
90 delete pVectorAdded;
91 pVectorAdded = NULL;
94 delete pLocation;
95 pLocation = NULL;
99 delete pCycle;
100 pCycle = NULL;
106 if (m_pSpan!= NULL)
108 delete m_pSpan;
109 m_pSpan = NULL;
114 // Returns an XML version of all our information
115 string CUserLogTrial::GetXML(const string& strPrefix)
117 //CFunctionLogger f1("CUserLogTrial::GetXML", g_pLogger);
119 string strResult = "";
121 string strPrefixTab = strPrefix;
122 strPrefixTab += "\t";
124 string strPrefixTabTab = strPrefixTab;
125 strPrefixTabTab += "\t";
127 strResult += strPrefix;
128 strResult += "<Trial>\n";
130 // Summarize what happened at the beginning of the block
131 strResult += GetSummaryXML(strPrefix);
133 // Parameters that we want tracked on a per trial basis
134 strResult += GetParamsXML(strPrefix);
136 // Size of the window and the canvas in screen coordniates
137 strResult += GetWindowCanvasXML(strPrefix);
139 strResult += m_strCurrentTrial;
141 // All the start and stop navigation events
142 strResult += GetNavCyclesXML(strPrefix);
144 strResult += strPrefix;
145 strResult += "</Trial>\n";
147 return strResult;
150 // See if any navigation has occured during this trial yet
151 bool CUserLogTrial::HasWritingOccured()
153 //CFunctionLogger f1("CUserLogTrial::HasWritingOccured", g_pLogger);
155 if (m_vpNavCycles.size() > 0)
156 return true;
157 return false;
162 void CUserLogTrial::StartWriting()
164 //CFunctionLogger f1("CUserLogTrial::StartWriting", g_pLogger);
166 if (m_bWritingStart)
168 g_pLogger->Log("CUserLogTrial::StartWriting, nav already marked as started!", logNORMAL);
169 return;
172 // Make sure our trial time span is running
173 if (m_pSpan != NULL)
174 m_pSpan->Continue();
176 // Start the task timer if we haven't already done so
177 if (m_pSpan == NULL)
178 m_pSpan = new CTimeSpan("Time", false);
180 if (m_pSpan == NULL)
182 g_pLogger->Log("CUserLogTrial::StartWriting, m_pSpan was NULL!", logNORMAL);
183 return;
186 m_bWritingStart = true;
188 // If we have already done some navigation, then the previous NavStop() would have stopped
189 // the timer in the last NavLocation object. We want to tell it to continue since the
190 // trial is not in fact over.
191 if (m_vpNavCycles.size() > 0)
193 NavLocation* pLastLocation = GetCurrentNavLocation();
194 if ((pLastLocation != NULL) && (pLastLocation->span != NULL))
195 pLastLocation->span->Continue();
198 // NavCycle* newCycle = AddNavCycle();
199 AddNavCycle();
202 void CUserLogTrial::StopWriting(double dBits)
204 //CFunctionLogger f1("CUserLogTrial::StopWriting", g_pLogger);
206 if (!m_bWritingStart)
208 g_pLogger->Log("CUserLogTrial::StopWriting, nav already marked as stopped!", logNORMAL);
209 return;
212 if (m_vpNavCycles.size() <= 0)
214 g_pLogger->Log("CUserLogTrial::StopWriting, vector was empty!", logNORMAL);
215 return;
218 NavCycle* pCycle = GetCurrentNavCycle();
219 if (pCycle == NULL)
221 g_pLogger->Log("CUserLogTrial::StopWriting, current cycle was NULL!", logNORMAL);
222 return;
225 CTimeSpan* pSpan = (CTimeSpan*) pCycle->pSpan;
226 if (pSpan == NULL)
228 g_pLogger->Log("CUserLogTrial::StopWriting, span was NULL!", logNORMAL);
229 return;
232 pCycle->dBits = dBits;
233 pSpan->Stop();
235 m_bWritingStart = false;
237 // Make sure the last location timer is stopped since this could be the end of the trial and
238 // we want the timestamps in the location elements to match the total trial time.
239 NavLocation* pLastLocation = GetCurrentNavLocation();
240 if ((pLastLocation != NULL) && (pLastLocation->span != NULL))
241 pLastLocation->span->Continue();
243 // Could be the last event of the trial
244 if (m_pSpan != NULL)
245 m_pSpan->Stop();
247 // We want to use the UserTrial info from the navigation period in Dasher. We'll update
248 // this everytime the user stops, this should make sure we get the right bit.
249 GetUserTrialInfo();
252 // The user has entered one or more new symbols. UserLog object will
253 // pass us the pointer to the current alphabet that is being used.
254 void CUserLogTrial::AddSymbols(Dasher::VECTOR_SYMBOL_PROB* vpNewSymbolProbs,
255 eUserLogEventType iEvent)
257 // Add the symbols to our running total of symbols.
259 // ACL: Old comment said "We track the symbols and not the display text
260 // since we may need to delete symbols later and
261 // a given symbol might take up multiple chars."
262 // - yet stored the display text????
264 // We also keep the probability around so we can
265 // calculate the average bits of the history.
266 m_vHistory.insert(m_vHistory.end(), vpNewSymbolProbs->begin(), vpNewSymbolProbs->end());
268 StopPreviousTimer();
270 // Create the new NavLocation struct that record the data about this addition
271 NavLocation* pLocation = NULL;
272 pLocation = new NavLocation;
274 if (pLocation == NULL)
276 g_pLogger->Log("CUserLogTrial::AddSymbols, failed to create location!", logNORMAL);
277 return;
280 pLocation->strHistory = GetHistoryDisplay();
281 pLocation->span = new CTimeSpan("Time", false);
282 pLocation->avgBits = GetHistoryAvgBits();
283 pLocation->event = iEvent;
284 pLocation->numDeleted = 0;
285 pLocation->pVectorAdded = new std::vector<Dasher::SymbolProb>(*vpNewSymbolProbs);
287 NavCycle* pCycle = GetCurrentNavCycle();
288 if (pCycle != NULL)
289 pCycle->vectorNavLocations.push_back(pLocation);
290 else
291 g_pLogger->Log("CUserLogTrial::AddSymbols, cycle was NULL!", logNORMAL);
295 void CUserLogTrial::DeleteSymbols(int iNumToDelete, eUserLogEventType iEvent)
297 //CFunctionLogger f1("CUserLogTrial::DeleteSymbols", g_pLogger);
299 if (iNumToDelete <= 0)
300 return;
302 // Be careful not to pop more things than we have (this will hork the
303 // memory up on linux but not windows).
304 int iActualNumToDelete = min((int) m_vHistory.size(), iNumToDelete);
306 for (int i = 0; i < iActualNumToDelete; i++)
308 // Remove the request number of symbols from our
309 // ongoing list.
310 m_vHistory.pop_back();
313 StopPreviousTimer();
315 // Create the new NavLocation struct that record the data about this addition
316 NavLocation* pLocation = NULL;
317 pLocation = new NavLocation;
319 if (pLocation == NULL)
321 g_pLogger->Log("CUserLogTrial::DeleteSymbols, failed to create location!", logNORMAL);
322 return;
325 pLocation->strHistory = GetHistoryDisplay();
326 pLocation->span = new CTimeSpan("Time", false);
327 pLocation->avgBits = GetHistoryAvgBits();
328 pLocation->event = iEvent;
329 pLocation->numDeleted = iNumToDelete;
330 pLocation->pVectorAdded = NULL;
332 NavCycle* pCycle = GetCurrentNavCycle();
333 if (pCycle != NULL)
334 pCycle->vectorNavLocations.push_back(pLocation);
335 else
336 g_pLogger->Log("CUserLogTrial::DeleteSymbols, cycle was NULL!", logNORMAL);
341 // Called by UserLog object whenever we move on to the next trial. This lets
342 // our trial object finalize any timers.
343 void CUserLogTrial::Done()
345 //CFunctionLogger f1("CUserLogTrial::Done", g_pLogger);
347 StopPreviousTimer();
349 // Stop the time span that tracks the total trial time (if not already stopped)
350 if ((m_pSpan != NULL) && (!m_pSpan->IsStopped()))
351 m_pSpan->Stop();
356 void CUserLogTrial::AddMouseLocation(int iX, int iY, float dNats)
358 //CFunctionLogger f1("CUserLogTrial::AddMouseLocation", g_pLogger);
360 CUserLocation* pLocation = NULL;
362 pLocation = new CUserLocation(iX, iY, dNats);
364 if (pLocation != NULL)
366 // m_vectorMouseLocations.push_back(location);
368 NavCycle* pCycle = GetCurrentNavCycle();
370 if (pCycle != NULL)
371 pCycle->vectorMouseLocations.push_back(pLocation);
372 else
373 g_pLogger->Log("CUserLogTrial::AddLocation, cycle was NULL!", logNORMAL);
375 else
376 g_pLogger->Log("CUserLogTrial::AddLocation, location was NULL!", logNORMAL);
380 // Adds a normalized version of our mouse coordinates based on the size
381 // of the window. Can optionally be told to store both representations.
382 void CUserLogTrial::AddMouseLocationNormalized(int iX, int iY, bool bStoreIntegerRep, float dNats)
384 //CFunctionLogger f1("CUserLogTrial::AddMouseLocationNormalized", g_pLogger);
386 CUserLocation* pLocation = NULL;
388 if ((m_sCanvasCoordinates.bottom == 0) &&
389 (m_sCanvasCoordinates.left == 0) &&
390 (m_sCanvasCoordinates.right == 0) &&
391 (m_sCanvasCoordinates.top == 0))
392 g_pLogger->Log("CUserLogTrial::AddMouseLocationNormalized, called before AddCanvasSize()?", logNORMAL);
394 pLocation = new CUserLocation(iX,
395 iY,
396 m_sCanvasCoordinates.top,
397 m_sCanvasCoordinates.left,
398 m_sCanvasCoordinates.bottom,
399 m_sCanvasCoordinates.right,
400 bStoreIntegerRep,
401 dNats);
403 if (pLocation != NULL)
405 // m_vectorMouseLocations.push_back(location);
406 NavCycle* pCycle = GetCurrentNavCycle();
408 if (pCycle != NULL)
409 pCycle->vectorMouseLocations.push_back(pLocation);
410 else
411 g_pLogger->Log("CUserLogTrial::AddMouseLocationNormalized, cycle was NULL!", logNORMAL);
413 else
414 g_pLogger->Log("CUserLogTrial::AddLocation, location was NULL!", logNORMAL);
417 void CUserLogTrial::AddKeyDown(int iId, int iType, int iEffect) {
418 CUserButton* pButton = new CUserButton(iId, iType, iEffect);
420 if(pButton) {
421 NavCycle* pCycle = GetCurrentNavCycle();
423 if(pCycle)
424 pCycle->vectorButtons.push_back(pButton);
425 else
426 g_pLogger->Log("CUserLogTrial::AddLocation, cycle was NULL!", logNORMAL);
428 else
429 g_pLogger->Log("CUserLogTrial::AddLocation, location was NULL!", logNORMAL);
432 // Sets the current window size, this includes area for the menu bar,
433 // sliders, canvas, etc.
434 void CUserLogTrial::AddWindowSize(int iTop, int iLeft, int iBottom, int iRight)
436 //CFunctionLogger f1("CUserLogTrial::AddWindowSize", g_pLogger);
438 m_sWindowCoordinates.top = iTop;
439 m_sWindowCoordinates.left = iLeft;
440 m_sWindowCoordinates.bottom = iBottom;
441 m_sWindowCoordinates.right = iRight;
444 // Sets the current canvas size
445 void CUserLogTrial::AddCanvasSize(int iTop, int iLeft, int iBottom, int iRight)
447 //CFunctionLogger f1("CUserLogTrial::AddCanvasSize", g_pLogger);
449 m_sCanvasCoordinates.top = iTop;
450 m_sCanvasCoordinates.left = iLeft;
451 m_sCanvasCoordinates.bottom = iBottom;
452 m_sCanvasCoordinates.right = iRight;
455 // Are we currently navigating?
456 bool CUserLogTrial::IsWriting()
458 return m_bWritingStart;
461 ////////////////////////////////////////// private methods ////////////////////////////////////////////////
463 void CUserLogTrial::InitMemberVars()
465 //CFunctionLogger f1("CUserLogTrial::InitMemberVars", g_pLogger);
467 m_bWritingStart = false;
468 m_pSpan = NULL;
469 m_strCurrentTrial = "";
471 m_sWindowCoordinates.bottom = 0;
472 m_sWindowCoordinates.top = 0;
473 m_sWindowCoordinates.left = 0;
474 m_sWindowCoordinates.right = 0;
476 m_sCanvasCoordinates.bottom = 0;
477 m_sCanvasCoordinates.top = 0;
478 m_sCanvasCoordinates.left = 0;
479 m_sCanvasCoordinates.right = 0;
482 // Obtain information that is being passed in from the UserTrial standalone application.
483 // This information tell us what the user is actually trying to enter.
484 void CUserLogTrial::GetUserTrialInfo()
486 //CFunctionLogger f1("CUserLogTrial::GetUserTrialInfo", g_pLogger);
488 m_strCurrentTrial = "";
490 if (m_strCurrentTrialFilename.length() > 0)
492 // We want ios::nocreate, but not available in .NET 2003, arrgh
493 fstream fin(m_strCurrentTrialFilename.c_str(), ios::in);
495 // Make sure we successfully opened before we start reading it
496 if (fin.is_open())
498 while (!fin.eof())
500 fin.getline(m_szTempBuffer, TEMP_BUFFER_SIZE);
501 if (strlen(m_szTempBuffer) > 0)
503 m_strCurrentTrial += "\t\t\t";
504 m_strCurrentTrial += m_szTempBuffer;
505 m_strCurrentTrial += "\n";
508 fin.close();
513 // Returns the concatenation of all our symbol history using
514 // the display text that the alphabet at the time of the
515 // symbol being added gave us.
516 string CUserLogTrial::GetHistoryDisplay()
518 //CFunctionLogger f1("CUserLogTrial::GetHistoryDisplay", g_pLogger);
520 string strResult = "";
522 for (unsigned int i = 0; i < m_vHistory.size(); i++)
524 Dasher::SymbolProb sItem = (Dasher::SymbolProb) m_vHistory[i];
525 strResult += sItem.strDisplay;
528 return strResult;
531 double CUserLogTrial::GetHistoryAvgBits()
533 //CFunctionLogger f1("CUserLogTrial::GetHistoryAvgBits", g_pLogger);
535 double dResult = 0.0;
537 if (m_vHistory.size() > 0)
539 for (unsigned int i = 0; i < m_vHistory.size(); i++)
541 Dasher::SymbolProb sItem = (Dasher::SymbolProb) m_vHistory[i];
543 dResult += log(sItem.prob);
545 dResult = dResult * -1.0;
546 dResult = dResult / log(2.0);
547 dResult = dResult / m_vHistory.size();
550 return dResult;
553 void CUserLogTrial::StopPreviousTimer()
555 //CFunctionLogger f1("CUserLogTrial::StopPreviousTimer", g_pLogger);
557 // Make sure the previous time span (if any) has had its timer stopped
558 if (m_vpNavCycles.size() > 0)
560 NavLocation* pLastLocation = GetCurrentNavLocation();
561 if ((pLastLocation != NULL) && (pLastLocation->span != NULL))
562 pLastLocation->span->Stop();
567 // Gets XML string for a given NavLocation struct
568 string CUserLogTrial::GetLocationXML(NavLocation* pLocation, const string& strPrefix)
570 //CFunctionLogger f1("CUserLogTrial::GetLocationXML", g_pLogger);
572 string strResult = "";
573 if (pLocation == NULL)
575 g_pLogger->Log("CUserLogTrial::GetLocationXML, location was NULL!", logNORMAL);
576 return strResult;
579 strResult += strPrefix;
580 strResult += "<Location>\n";
582 strResult += strPrefix;
583 strResult += "\t<History>";
584 strResult += pLocation->strHistory;
585 strResult += "</History>\n";
587 strResult += strPrefix;
588 strResult += "\t<AvgBits>";
589 sprintf(m_szTempBuffer, "%0.6f", pLocation->avgBits);
590 strResult += m_szTempBuffer;
591 strResult += "</AvgBits>\n";
593 // Only output the event if it is interesting type, not normal mouse navigation
594 if (pLocation->event != userLogEventMouse)
596 strResult += strPrefix;
597 strResult += "\t\t<Event>";
598 sprintf(m_szTempBuffer, "%d", (int) pLocation->event);
599 strResult += m_szTempBuffer;
600 strResult += "</Event>\n";
603 if ((pLocation->pVectorAdded != NULL) && (pLocation->pVectorAdded->size() > 0))
605 strResult += strPrefix;
606 strResult += "\t<NumAdded>";
607 sprintf(m_szTempBuffer, "%zu", pLocation->pVectorAdded->size());
608 strResult += m_szTempBuffer;
609 strResult += "</NumAdded>\n";
611 Dasher::VECTOR_SYMBOL_PROB* pVectorAdded = pLocation->pVectorAdded;
613 if (pVectorAdded != NULL)
615 // Output the details of each add
616 for (unsigned int j = 0; j < pVectorAdded->size(); j++)
618 Dasher::SymbolProb sItem = (Dasher::SymbolProb) (*pVectorAdded)[j];
620 strResult += strPrefix;
621 strResult += "\t<Add>\n";
623 strResult += strPrefix;
624 strResult += "\t\t<Text>";
625 strResult += sItem.strDisplay;
626 strResult += "</Text>\n";
628 strResult += strPrefix;
629 strResult += "\t\t<Prob>";
630 sprintf(m_szTempBuffer, "%0.6f", sItem.prob);
631 strResult += m_szTempBuffer;
632 strResult += "</Prob>\n";
634 strResult += strPrefix;
635 strResult += "\t</Add>\n";
640 if (pLocation->numDeleted > 0)
642 strResult += strPrefix;
643 strResult += "\t<NumDeleted>";
644 sprintf(m_szTempBuffer, "%d", pLocation->numDeleted);
645 strResult += m_szTempBuffer;
646 strResult += "</NumDeleted>\n";
649 if (pLocation->span != NULL)
651 string strPrefixTabTabTab = strPrefix;
652 strPrefixTabTabTab += "\t";
654 strResult += pLocation->span->GetXML(strPrefixTabTabTab);
657 strResult += strPrefix;
658 strResult += "</Location>\n";
660 return strResult;
663 // Output the XML for the summary section of XML
664 string CUserLogTrial::GetSummaryXML(const string& strPrefix)
666 //CFunctionLogger f1("CUserLogTrial::GetSummaryXML", g_pLogger);
668 string strResult = "";
670 strResult += strPrefix;
671 strResult += "\t<Summary>\n";
673 // Figure out what the user ended up writing and how fast they did it
674 string strText = "";
675 double dAvgBits = 0.0;
677 NavLocation* pLocation = GetCurrentNavLocation();
678 if (pLocation != NULL)
680 strText = GetHistoryDisplay();
681 dAvgBits = pLocation->avgBits;
684 int iButtonCount = GetButtonCount();
685 double dTotalBits = GetTotalBits();
687 strResult += GetStatsXML(strPrefix, strText, m_pSpan, dAvgBits, iButtonCount, dTotalBits);
689 strResult += strPrefix;
690 strResult += "\t</Summary>\n";
692 return strResult;
695 // Calculates the various summary stats we output
696 string CUserLogTrial::GetStatsXML(const string& strPrefix, const string& strText, CTimeSpan* pSpan, double dAvgBits, int iButtonCount, double dTotalBits)
698 //CFunctionLogger f1("CUserLogTrial::GetStatsXML", g_pLogger);
700 string strResult = "";
702 if (pSpan == NULL)
704 g_pLogger->Log("CUserLogTrial::GetStatsXML, pSpan = NULL!", logNORMAL);
705 return strResult;
708 strResult += strPrefix;
709 strResult += "\t\t<Text>";
710 strResult += strText;
711 strResult += "</Text>\n";
713 // Average number of bits along the path to the final string
714 strResult += strPrefix;
715 strResult += "\t\t<AvgBits>";
716 sprintf(m_szTempBuffer, "%0.6f", dAvgBits);
717 strResult += m_szTempBuffer;
718 strResult += "</AvgBits>\n";
720 strResult += strPrefix;
721 strResult += "\t\t<TotalBits>";
722 sprintf(m_szTempBuffer, "%0.6f", dTotalBits);
723 strResult += m_szTempBuffer;
724 strResult += "</TotalBits>\n";
727 strResult += strPrefix;
728 strResult += "\t\t<ButtonCount>";
729 sprintf(m_szTempBuffer, "%d", iButtonCount);
730 strResult += m_szTempBuffer;
731 strResult += "</ButtonCount>\n";
733 // Calculate the number of words and characters
734 strResult += strPrefix;
735 strResult += "\t\t<Chars>";
737 // We want the number of symbols which might differ
738 // from the actual length of the text history.
739 int iNumChars = m_vHistory.size();
740 sprintf(m_szTempBuffer, "%d", iNumChars);
741 strResult += m_szTempBuffer;
742 strResult += "</Chars>\n";
744 strResult += strPrefix;
745 strResult += "\t\t<Words>";
746 double dNumWords = (double) iNumChars / (double) 5;
747 sprintf(m_szTempBuffer, "%0.2f", dNumWords);
748 strResult += m_szTempBuffer;
749 strResult += "</Words>\n";
751 double dWPM = 0.0;
752 double dCPM = 0.0;
754 if (m_pSpan != NULL)
756 dWPM = (double) dNumWords / (m_pSpan->GetElapsed() / 60.0);
757 dCPM = (double) iNumChars / (m_pSpan->GetElapsed() / 60.0);
760 strResult += strPrefix;
761 strResult += "\t\t<WPM>";
762 sprintf(m_szTempBuffer, "%0.3f", dWPM);
763 strResult += m_szTempBuffer;
764 strResult += "</WPM>\n";
766 strResult += strPrefix;
767 strResult += "\t\t<CPM>";
768 sprintf(m_szTempBuffer, "%0.3f", dCPM);
769 strResult += m_szTempBuffer;
770 strResult += "</CPM>\n";
772 string strPrefixTabTab = strPrefix;
773 strPrefixTabTab += "\t\t";
775 if (m_pSpan != NULL)
776 strResult += m_pSpan->GetXML(strPrefixTabTab);
778 return strResult;
781 string CUserLogTrial::GetWindowCanvasXML(const string& strPrefix)
783 //CFunctionLogger f1("CUserLogTrial::GetWindowCanvasXML", g_pLogger);
785 string strResult = "";
787 // Log the window location and size that was last used during this trial
788 strResult += strPrefix;
789 strResult += "\t<WindowCoordinates>\n";
791 strResult += strPrefix;
792 sprintf(m_szTempBuffer, "\t\t<Top>%d</Top>\n", m_sWindowCoordinates.top);
793 strResult += m_szTempBuffer;
795 strResult += strPrefix;
796 sprintf(m_szTempBuffer, "\t\t<Bottom>%d</Bottom>\n", m_sWindowCoordinates.bottom);
797 strResult += m_szTempBuffer;
799 strResult += strPrefix;
800 sprintf(m_szTempBuffer, "\t\t<Left>%d</Left>\n", m_sWindowCoordinates.left);
801 strResult += m_szTempBuffer;
803 strResult += strPrefix;
804 sprintf(m_szTempBuffer, "\t\t<Right>%d</Right>\n", m_sWindowCoordinates.right);
805 strResult += m_szTempBuffer;
807 strResult += strPrefix;
808 strResult += "\t</WindowCoordinates>\n";
810 // Log the canvas location and size that was last used during this trial
811 strResult += strPrefix;
812 strResult += "\t<CanvasCoordinates>\n";
814 strResult += strPrefix;
815 sprintf(m_szTempBuffer, "\t\t<Top>%d</Top>\n", m_sCanvasCoordinates.top);
816 strResult += m_szTempBuffer;
818 strResult += strPrefix;
819 sprintf(m_szTempBuffer, "\t\t<Bottom>%d</Bottom>\n", m_sCanvasCoordinates.bottom);
820 strResult += m_szTempBuffer;
822 strResult += strPrefix;
823 sprintf(m_szTempBuffer, "\t\t<Left>%d</Left>\n", m_sCanvasCoordinates.left);
824 strResult += m_szTempBuffer;
826 strResult += strPrefix;
827 sprintf(m_szTempBuffer, "\t\t<Right>%d</Right>\n", m_sCanvasCoordinates.right);
828 strResult += m_szTempBuffer;
830 strResult += strPrefix;
831 strResult += "\t</CanvasCoordinates>\n";
833 return strResult;
836 string CUserLogTrial::GetParamsXML(const string& strPrefix)
838 //CFunctionLogger f1("CUserLogTrial::GetParamsXML", g_pLogger);
840 string strResult = "";
842 if (m_vpParams.size() > 0)
844 // Make parameters with the same name appear near each other in the results
845 sort(m_vpParams.begin(), m_vpParams.end(), CUserLogParam::ComparePtr);
847 strResult += strPrefix;
848 strResult += "\t<Params>\n";
850 string strPrefixPlusTabTab = strPrefix;
851 strPrefixPlusTabTab += "\t\t";
853 for (unsigned int i = 0; i < m_vpParams.size(); i++)
855 CUserLogParam* pParam = (CUserLogParam*) m_vpParams[i];
857 strResult += GetParamXML(pParam, strPrefixPlusTabTab);
860 strResult += strPrefix;
861 strResult += "\t</Params>\n";
864 return strResult;
868 int CUserLogTrial::GetButtonCount() {
869 int iCount(0);
871 for(VECTOR_NAV_CYCLE_PTR::iterator it(m_vpNavCycles.begin()); it != m_vpNavCycles.end(); ++it)
872 for(VECTOR_USER_BUTTON_PTR::iterator it2((*it)->vectorButtons.begin()); it2 != (*it)->vectorButtons.end(); ++it2)
873 iCount += (*it2)->GetCount();
875 return iCount;
878 double CUserLogTrial::GetTotalBits() {
879 double dBits(0.0);
881 for(VECTOR_NAV_CYCLE_PTR::iterator it(m_vpNavCycles.begin()); it != m_vpNavCycles.end(); ++it)
882 dBits += (*it)->dBits;
884 return dBits;
887 // Parameters can optionally be specified to be added to the Trial objects.
888 // This allows us to easily see what a certain parameter value was used
889 // in a given trial.
890 void CUserLogTrial::AddParam(const string& strName, const string& strValue, int iOptionMask)
892 //CFunctionLogger f1("CUserLogTrial::AddParam", g_pLogger);
894 bool bTrackMultiple = false;
896 if (iOptionMask & userLogParamTrackMultiple)
897 bTrackMultiple = true;
899 // See if this matches an existing parameter value that we may want to
900 // overwrite. But only if we aren't suppose to keep track of multiple changes.
901 if (!bTrackMultiple)
903 for (unsigned int i = 0; i < m_vpParams.size(); i++)
905 CUserLogParam* pParam = (CUserLogParam*) m_vpParams[i];
907 if (pParam != NULL)
909 if (pParam->strName.compare(strName) == 0)
911 pParam->strValue = strValue;
912 return;
917 // We need to add a new param
918 CUserLogParam* pNewParam = new CUserLogParam;
919 if (pNewParam == NULL)
921 g_pLogger->Log("CUserLogTrial::AddParam, newParam was NULL!", logNORMAL);
922 return;
925 pNewParam->strName = strName;
926 pNewParam->strValue = strValue;
927 pNewParam->strTimeStamp = "";
929 // Parameters that can have multiple values logged will also log when they were changed
930 if (bTrackMultiple)
931 pNewParam->strTimeStamp = CTimeSpan::GetTimeStamp();
933 m_vpParams.push_back(pNewParam);
936 // Static method that generates the XML representation of a
937 // single param name value set. Used both to output params
938 // for a trial and for the parent UserLog object.
939 string CUserLogTrial::GetParamXML(CUserLogParam* pParam, const string& strPrefix)
941 //CFunctionLogger f1("CUserLogTrial::GetParamXML", g_pLogger);
943 string strResult = "";
945 if (pParam != NULL)
947 strResult += strPrefix;
948 strResult += "<";
949 strResult += pParam->strName;
950 strResult += ">";
952 if (pParam->strTimeStamp.length() > 0)
954 strResult += "\n";
955 strResult += strPrefix;
956 strResult += "\t<Value>";
957 strResult += pParam->strValue;
958 strResult += "</Value>\n";
960 strResult += strPrefix;
961 strResult += "\t<Time>";
962 strResult += pParam->strTimeStamp;
963 strResult += "</Time>\n";
965 strResult += strPrefix;
967 else
969 strResult += pParam->strValue;
972 strResult += "</";
973 strResult += pParam->strName;
974 strResult += ">\n";
977 return strResult;
980 // Returns a pointer to the currently active navigation cycle
981 NavCycle* CUserLogTrial::GetCurrentNavCycle()
983 //CFunctionLogger f1("CUserLogTrial::GetCurrentNavCycle", g_pLogger);
985 if (m_vpNavCycles.size() <= 0)
986 return NULL;
987 return m_vpNavCycles[m_vpNavCycles.size() - 1];
990 // Gets a pointer to the last NavLocation object
991 // in the current navication cycle.
992 NavLocation* CUserLogTrial::GetCurrentNavLocation()
994 //CFunctionLogger f1("CUserLogTrial::GetCurrentNavLocation", g_pLogger);
996 // NavCycle* pCycle = GetCurrentNavCycle();
998 // if (pCycle == NULL)
999 // return NULL;
1001 // if (pCycle->vectorNavLocations.size() <= 0)
1002 // return NULL;
1004 // return (NavLocation*) pCycle->vectorNavLocations[pCycle->vectorNavLocations.size() - 1];
1006 // New version - reverse iterate through the list and find the last nav cycle which has any locations
1008 for(VECTOR_NAV_CYCLE_PTR::reverse_iterator it(m_vpNavCycles.rbegin()); it != m_vpNavCycles.rend(); ++it) {
1009 if((*it)->vectorNavLocations.size() > 0)
1010 return (NavLocation*) (*it)->vectorNavLocations[(*it)->vectorNavLocations.size() - 1];
1013 return NULL;
1016 // Adds a new navgiation cycle to our collection
1017 NavCycle* CUserLogTrial::AddNavCycle()
1019 //CFunctionLogger f1("CUserLogTrial::AddNavCycle", g_pLogger);
1021 NavCycle* pNewCycle = new NavCycle;
1022 if (pNewCycle == NULL)
1024 g_pLogger->Log("CUserLogTrial::AddNavCycle, failed to create NavCycle!", logNORMAL);
1025 return NULL;
1028 pNewCycle->pSpan = new CTimeSpan("Time", false);
1030 m_vpNavCycles.push_back(pNewCycle);
1031 return pNewCycle;
1034 string CUserLogTrial::GetNavCyclesXML(const string& strPrefix)
1036 //CFunctionLogger f1("CUserLogTrial::GetNavCyclesXML", g_pLogger);
1038 string strResult = "";
1040 string strPrefixTab = strPrefix;
1041 strPrefixTab += "\t";
1043 string strPrefixTabTab = strPrefixTab;
1044 strPrefixTabTab += "\t";
1046 string strPrefixTabTabTab = strPrefixTabTab;
1047 strPrefixTabTabTab += "\t";
1049 string strPrefixTabTabTabTab = strPrefixTabTabTab;
1050 strPrefixTabTabTabTab += "\t";
1052 strResult += strPrefixTab;
1053 strResult += "<Navs>\n";
1055 for (unsigned int i = 0; i < m_vpNavCycles.size(); i++)
1057 NavCycle* pCycle = (NavCycle*) m_vpNavCycles[i];
1059 if (pCycle != NULL)
1061 strResult += strPrefixTabTab;
1062 strResult += "<Nav>\n";
1064 if (pCycle->pSpan != NULL)
1065 strResult += pCycle->pSpan->GetXML(strPrefixTabTabTab);
1067 if (pCycle->vectorNavLocations.size() > 0)
1069 strResult += strPrefixTabTabTab;
1070 strResult += "<Locations>\n";
1072 for (unsigned int i = 0; i < pCycle->vectorNavLocations.size(); i++)
1074 NavLocation* pLocation = (NavLocation*) pCycle->vectorNavLocations[i];
1076 if (pLocation != NULL)
1077 strResult += GetLocationXML(pLocation, strPrefixTabTabTabTab);
1079 strResult += strPrefixTabTabTab;
1080 strResult += "</Locations>\n";
1083 if (pCycle->vectorMouseLocations.size() > 0)
1085 strResult += strPrefixTabTabTab;
1086 strResult += "<MousePositions>\n";
1088 for (unsigned int i = 0; i < pCycle->vectorMouseLocations.size(); i++)
1090 CUserLocation* pLocation = (CUserLocation*) pCycle->vectorMouseLocations[i];
1092 if (pLocation != NULL)
1094 strResult += pLocation->GetXML(strPrefixTabTabTabTab);
1098 strResult += strPrefixTabTabTab;
1099 strResult += "</MousePositions>\n";
1102 if (pCycle->vectorButtons.size() > 0)
1104 strResult += strPrefixTabTabTab;
1105 strResult += "<Buttons>\n";
1107 for (unsigned int i = 0; i < pCycle->vectorButtons.size(); i++)
1109 CUserButton* pButton = (CUserButton*) pCycle->vectorButtons[i];
1111 if(pButton)
1113 strResult += pButton->GetXML(strPrefixTabTabTabTab);
1117 strResult += strPrefixTabTabTab;
1118 strResult += "</Buttons>\n";
1121 strResult += strPrefixTabTab;
1122 strResult += "</Nav>\n";
1127 strResult += strPrefixTab;
1128 strResult += "</Navs>\n";
1130 return strResult;
1133 // Construct based on some XML, second parameter is just to make signature
1134 // different from the normal constructor.
1135 CUserLogTrial::CUserLogTrial(const string& strXML, int iIgnored)
1137 //CFunctionLogger f1("CUserLogTrial::CUserLogTrial(XML)", g_pLogger);
1139 InitMemberVars();
1140 VECTOR_STRING vNavs;
1142 string strParams = XMLUtil::GetElementString("Params", strXML, true);
1143 string strWindow = XMLUtil::GetElementString("WindowCoordinates", strXML, true);
1144 string strCanvas = XMLUtil::GetElementString("CanvasCoordinates", strXML, true);
1145 string strNavs = XMLUtil::GetElementString("Navs", strXML, true);
1146 string strSummary = XMLUtil::GetElementString("Summary", strXML, true);
1147 string strSummaryTime = XMLUtil::GetElementString("Time", strSummary, true);
1148 vNavs = XMLUtil::GetElementStrings("Nav", strNavs, true);
1150 string strCurrentTrial = XMLUtil::GetElementString("CurrentTrial", strXML, false);
1151 if (strCurrentTrial.length() > 0)
1153 // We copied the XML string directly into the member variable
1154 // including the start/end tag, so we need to reproduce the
1155 // tags ourselves.
1156 m_strCurrentTrial = "\t\t\t<CurrentTrial>\n";
1157 m_strCurrentTrial += strCurrentTrial;
1158 m_strCurrentTrial += "</CurrentTrial>\n";
1161 m_vpParams = ParseParamsXML(strParams);
1162 m_sWindowCoordinates = ParseWindowXML(strWindow);
1163 m_sCanvasCoordinates = ParseWindowXML(strCanvas);
1164 m_pSpan = new CTimeSpan("Time", strSummaryTime);
1166 // Process each <Nav> tag
1167 string strTime = "";
1168 string strLocations = "";
1169 string strMousePositions = "";
1171 VECTOR_STRING vLocations;
1172 VECTOR_STRING vMousePositions;
1173 VECTOR_STRING vAdded;
1175 for (VECTOR_STRING_ITER iter = vNavs.begin(); iter < vNavs.end(); iter++)
1177 strTime = "";
1178 strLocations = "";
1179 strMousePositions = "";
1180 vLocations.erase(vLocations.begin(), vLocations.end());
1181 vMousePositions.erase(vMousePositions.begin(), vMousePositions.end());
1183 strTime = XMLUtil::GetElementString("Time", *iter, true);
1184 strLocations = XMLUtil::GetElementString("Locations", *iter, true);
1185 strMousePositions = XMLUtil::GetElementString("MousePositions", *iter, true);
1187 NavCycle* pCycle = new NavCycle();
1188 if (pCycle == NULL)
1190 g_pLogger->Log("CUserLogTrial::CUserLogTrial, failed to create NavCycle!", logNORMAL);
1191 return;
1194 pCycle->pSpan = NULL;
1196 if (strTime.length() > 0)
1198 pCycle->pSpan = new CTimeSpan("Time", strTime);
1200 if (strLocations.length() > 0)
1202 vLocations = XMLUtil::GetElementStrings("Location", strLocations, true);
1204 for (VECTOR_STRING_ITER iter2 = vLocations.begin(); iter2 < vLocations.end(); iter2++)
1206 vAdded.erase(vAdded.begin(), vAdded.end());
1208 NavLocation* pLocation = new NavLocation();
1209 if (pLocation == NULL)
1211 g_pLogger->Log("CUserLogTrial::CUserLogTrial, failed to create NavLocation!", logNORMAL);
1212 return;
1215 pLocation->strHistory = XMLUtil::GetElementString("History", *iter2);
1216 pLocation->avgBits = (double) XMLUtil::GetElementFloat("AvgBits", *iter2);
1217 pLocation->event = (eUserLogEventType) XMLUtil::GetElementInt("Event", *iter2);
1218 pLocation->numDeleted = XMLUtil::GetElementInt("NumDeleted", *iter2);
1220 pLocation->span = NULL;
1221 string strTime = XMLUtil::GetElementString("Time", *iter2);
1222 pLocation->span = new CTimeSpan("Time", strTime);
1224 // Handle the multiple <Add> tags that might exist
1225 vAdded = XMLUtil::GetElementStrings("Add", *iter2);
1226 pLocation->pVectorAdded = new Dasher::VECTOR_SYMBOL_PROB;
1228 for (VECTOR_STRING_ITER iter3 = vAdded.begin(); iter3 < vAdded.end(); iter3++)
1230 Dasher::SymbolProb sAdd(0, // We don't have the original integer symbol index
1231 XMLUtil::GetElementString("Text", *iter3),
1232 XMLUtil::GetElementFloat("Prob", *iter3));
1234 if (pLocation->pVectorAdded != NULL)
1235 pLocation->pVectorAdded->push_back(sAdd);
1237 // Also track it in one complete vector of all the adds
1238 m_vHistory.push_back(sAdd);
1241 // If this was a deleted event, then we need to erase some stuff from the running history
1242 // Be careful not to pop more things than we have (this will hork the
1243 // memory up on linux but not windows).
1244 int iActualNumToDelete = min((int) m_vHistory.size(), pLocation->numDeleted);
1245 for (int i = 0; i < iActualNumToDelete; i++)
1246 m_vHistory.pop_back();
1248 pCycle->vectorNavLocations.push_back(pLocation);
1252 if (strMousePositions.length() > 0)
1254 vMousePositions = XMLUtil::GetElementStrings("Pos", strMousePositions, true);
1255 for (VECTOR_STRING_ITER iter2 = vMousePositions.begin(); iter2 < vMousePositions.end(); iter2++)
1257 CUserLocation* pLocation = new CUserLocation(*iter2);
1258 pCycle->vectorMouseLocations.push_back(pLocation);
1263 m_vpNavCycles.push_back(pCycle);
1268 // Helper that parses parameters out of the XML block, used by UserLog
1269 // and by UserLogTrial to do the same thing.
1270 VECTOR_USER_LOG_PARAM_PTR CUserLogTrial::ParseParamsXML(const string& strXML)
1272 //CFunctionLogger f1("CUserLogTrial::ParseParamsXML", g_pLogger);
1274 VECTOR_USER_LOG_PARAM_PTR vResult;
1275 VECTOR_NAME_VALUE_PAIR vParams;
1277 vParams = XMLUtil::GetNameValuePairs(strXML, true);
1279 // Handle adding all the name/value parameter pairs. XML looks like:
1280 // <Eyetracker>0</Eyetracker>
1281 // <MaxBitRate>
1282 // <Value>7.0100</Value>
1283 // <Time>15:48:53.140</Time>
1284 // </MaxBitRate>
1285 for (VECTOR_NAME_VALUE_PAIR_ITER iter = vParams.begin(); iter < vParams.end(); iter++)
1287 CUserLogParam* pParam = new CUserLogParam();
1289 if (pParam != NULL)
1291 pParam->strName = iter->strName;
1293 // See if we have a type that has a timestamp
1294 string strValue = XMLUtil::GetElementString("Value", iter->strValue, true);
1295 string strTime = XMLUtil::GetElementString("Time", iter->strValue, true);
1297 if ((strValue.length() > 0) || (strTime.length() > 0))
1299 pParam->strValue = strValue;
1300 pParam->strTimeStamp = strTime;
1302 else
1303 pParam->strValue = iter->strValue;
1305 pParam->options = 0;
1307 vResult.push_back(pParam);
1311 return vResult;
1314 // Parse our window or canvas coorindates from XML
1315 WindowSize CUserLogTrial::ParseWindowXML(const string& strXML)
1317 //CFunctionLogger f1("CUserLogTrial::ParseWindowXML", g_pLogger);
1319 WindowSize sResult;
1321 sResult.top = XMLUtil::GetElementInt("Top", strXML);
1322 sResult.bottom = XMLUtil::GetElementInt("Bottom", strXML);
1323 sResult.left = XMLUtil::GetElementInt("Left", strXML);
1324 sResult.right = XMLUtil::GetElementInt("Right", strXML);
1326 return sResult;
1329 // Returns a vector that contains the tab delimited mouse
1330 // coordinates for each of our navigation cycles.
1331 VECTOR_STRING CUserLogTrial::GetTabMouseXY(bool bReturnNormalized)
1333 //CFunctionLogger f1("CUserLogTrial::GetTabMouseXY", g_pLogger);
1335 VECTOR_STRING vResult;
1336 for (VECTOR_NAV_CYCLE_PTR_ITER iter = m_vpNavCycles.begin(); iter < m_vpNavCycles.end(); iter++)
1338 string strResult = "";
1340 if (*iter != NULL)
1342 for (VECTOR_USER_LOCATION_PTR_ITER iter2 = (*iter)->vectorMouseLocations.begin();
1343 iter2 < (*iter)->vectorMouseLocations.end();
1344 iter2++)
1346 if (*iter2 != NULL)
1347 strResult += (*iter2)->GetTabMouseXY(bReturnNormalized);
1351 vResult.push_back(strResult);
1354 return vResult;
1357 // Calculates the mouse density with a given grid size number of buckets.
1358 // Each element of the vector is a 2D array of double values from 0.0 - 1.0.
1360 // NOTE: We allocate the memory here for the double**, our caller must
1361 // handle freeing it!
1362 VECTOR_DENSITY_GRIDS CUserLogTrial::GetMouseDensity(int iGridSize)
1364 //CFunctionLogger f1("CUserLogTrial::GetMouseDensity", g_pLogger);
1366 VECTOR_DENSITY_GRIDS vResult;
1368 for (VECTOR_NAV_CYCLE_PTR_ITER iter = m_vpNavCycles.begin(); iter < m_vpNavCycles.end(); iter++)
1370 if (*iter != NULL)
1372 DENSITY_GRID ppGrid;
1374 // Init the grid with all 0.0 values
1375 ppGrid = new double*[iGridSize];
1376 for (int i = 0; i < iGridSize; i++)
1377 ppGrid[i] = new double[iGridSize];
1379 for (int i = 0; i < iGridSize; i++)
1380 for (int j = 0; j < iGridSize; j++)
1381 ppGrid[i][j] = 0.0;
1383 unsigned int iCount = 0;
1385 // Assign each mouse to location to one of the buckets and increment
1386 // the count on that bucket.
1387 for (VECTOR_USER_LOCATION_PTR_ITER iter2 = (*iter)->vectorMouseLocations.begin();
1388 iter2 < (*iter)->vectorMouseLocations.end();
1389 iter2++)
1391 if (*iter2 != NULL)
1393 int i = 0;
1394 int j = 0;
1395 (*iter2)->GetMouseGridLocation(iGridSize, &i, &j);
1396 // Increment the count on this location, we'll throw away points
1397 // that were outside the canvas grid.
1398 if ((i < iGridSize) && (j < iGridSize) && (i >= 0) && (j >= 0))
1400 // We reverse j and i to get x to increase left top right
1401 // and y from top to bottom
1402 ppGrid[j][i] = ppGrid[j][i] + 1.0;
1403 iCount++;
1407 // Now normalize everything so each grid location is a
1408 // percentage of the time we spent in that location.
1409 for (int i = 0; i < iGridSize; i++)
1412 for (int j = 0; j < iGridSize; j++)
1414 ppGrid[i][j] = ppGrid[i][j] / (double) iCount;
1419 vResult.push_back(ppGrid);
1423 return vResult;
1426 // Merge the density of two grids together. This is done but adding their values
1427 // and dividing by two. If either pointer is NULL, then we return the other grid
1428 // values intact. We free the memory in our parameter grids.
1429 DENSITY_GRID CUserLogTrial::MergeGrids(int iGridSize, DENSITY_GRID ppGridA, DENSITY_GRID ppGridB)
1431 //CFunctionLogger f1("CUserLogTrial::MergeGrids", g_pLogger);
1433 DENSITY_GRID ppResult;
1434 ppResult = new double*[iGridSize];
1435 for (int i = 0; i < iGridSize; i++)
1436 ppResult[i] = new double[iGridSize];
1438 for (int i = 0; i < iGridSize; i++)
1439 for (int j = 0; j < iGridSize; j++)
1440 ppResult[i][j] = 0.0;
1442 // Both NULL, then return grid of all 0.0's
1443 if ((ppGridA == NULL) && (ppGridB == NULL))
1444 return ppResult;
1446 if (ppGridA == NULL)
1448 // grid A is NULL, return copy of grid B
1449 for (int i = 0; i < iGridSize; i++)
1451 for (int j = 0; j < iGridSize; j++)
1452 ppResult[i][j] = ppGridB[i][j];
1455 else if (ppGridB == NULL)
1457 // grid B is NULL, return copy of grid A
1458 for (int i = 0; i < iGridSize; i++)
1460 for (int j = 0; j < iGridSize; j++)
1461 ppResult[i][j] = ppGridA[i][j];
1465 else
1467 // Normal case, merge the two density grids
1468 for (int i = 0; i < iGridSize; i++)
1470 for (int j = 0; j < iGridSize; j++)
1471 ppResult[i][j] = (ppGridA[i][j] + ppGridB[i][j]) / 2.0;
1475 if (ppGridA != NULL)
1477 for (int i = 0; i < iGridSize; i++)
1479 if (ppGridA[i] != NULL)
1481 delete ppGridA[i];
1482 ppGridA[i] = NULL;
1485 delete ppGridA;
1486 ppGridA = NULL;
1489 if (ppGridB != NULL)
1491 for (int i = 0; i < iGridSize; i++)
1493 if (ppGridB[i] != NULL)
1495 delete ppGridB[i];
1496 ppGridB[i] = NULL;
1499 delete ppGridB;
1500 ppGridB = NULL;
1503 return ppResult;