529 lines
12 KiB
C++
529 lines
12 KiB
C++
// CsvTestDlg.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "CsvTest.h"
|
|
#include "CsvTestDlg.h"
|
|
#include "OXCsvFile.h"
|
|
#include "OXTrace.h"
|
|
#include "utsampleabout.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CCsvTestDlg dialog
|
|
|
|
CCsvTestDlg::CCsvTestDlg(CWnd* pParent /*=NULL*/)
|
|
: CDialog(CCsvTestDlg::IDD, pParent)
|
|
{
|
|
//{{AFX_DATA_INIT(CCsvTestDlg)
|
|
m_bUseHeaders = FALSE;
|
|
m_strData = _T("");
|
|
//}}AFX_DATA_INIT
|
|
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
|
}
|
|
|
|
void CCsvTestDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CCsvTestDlg)
|
|
DDX_Control(pDX, IDC_SAVE, m_ctrlSave);
|
|
DDX_Check(pDX, IDC_USE_HEADERS, m_bUseHeaders);
|
|
DDX_Text(pDX, IDC_DATA, m_strData);
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CCsvTestDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CCsvTestDlg)
|
|
ON_WM_SYSCOMMAND()
|
|
ON_WM_PAINT()
|
|
ON_WM_QUERYDRAGICON()
|
|
ON_BN_CLICKED(IDC_SAVE, OnSave)
|
|
ON_BN_CLICKED(IDC_ABOUT, OnAbout)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CCsvTestDlg message handlers
|
|
|
|
BOOL CCsvTestDlg::OnInitDialog()
|
|
{
|
|
CDialog::OnInitDialog();
|
|
|
|
m_ctrlSave.EnableWindow(FALSE);
|
|
|
|
// Add "About..." menu item to system menu.
|
|
|
|
// IDM_ABOUTBOX must be in the system command range.
|
|
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
|
|
ASSERT(IDM_ABOUTBOX < 0xF000);
|
|
|
|
CMenu* pSysMenu = GetSystemMenu(FALSE);
|
|
if (pSysMenu != NULL)
|
|
{
|
|
CString strAboutMenu;
|
|
strAboutMenu.LoadString(IDS_ABOUTBOX);
|
|
if (!strAboutMenu.IsEmpty())
|
|
{
|
|
pSysMenu->AppendMenu(MF_SEPARATOR);
|
|
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
|
|
}
|
|
}
|
|
|
|
// Set the icon for this dialog. The framework does this automatically
|
|
// when the application's main window is not a dialog
|
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
|
|
|
// TODO: Add extra initialization here
|
|
OXTRACE_SETDUMPFILE(_T("trace.txt"));
|
|
OXTRACE_SETPREFIX(TRUE);
|
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
}
|
|
|
|
void CCsvTestDlg::OnSysCommand(UINT nID, LPARAM lParam)
|
|
{
|
|
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
|
|
{
|
|
OnAbout();
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnSysCommand(nID, lParam);
|
|
}
|
|
}
|
|
|
|
// If you add a minimize button to your dialog, you will need the code below
|
|
// to draw the icon. For MFC applications using the document/view model,
|
|
// this is automatically done for you by the framework.
|
|
|
|
void CCsvTestDlg::OnPaint()
|
|
{
|
|
if (IsIconic())
|
|
{
|
|
CPaintDC dc(this); // device context for painting
|
|
|
|
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
|
|
|
|
// Center icon in client rectangle
|
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
|
|
// Draw the icon
|
|
dc.DrawIcon(x, y, m_hIcon);
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnPaint();
|
|
}
|
|
}
|
|
|
|
// The system calls this to obtain the cursor to display while the user drags
|
|
// the minimized window.
|
|
HCURSOR CCsvTestDlg::OnQueryDragIcon()
|
|
{
|
|
return (HCURSOR) m_hIcon;
|
|
}
|
|
|
|
static LPCTSTR headers[]={ _T("ID"), _T("Name"), _T("Byte"), _T("Integer"),
|
|
_T("Float"), _T("Double"), _T("String"),
|
|
_T("Valid"), NULL };
|
|
|
|
static LPCTSTR aliases[]={ _T("Int"), _T("I"), NULL };
|
|
|
|
static const int nColumns=sizeof(headers) / sizeof(headers[0]) - 1;
|
|
|
|
static LPCTSTR headers2[]={ _T("ID"), _T("Last Name"), _T("Address"), NULL };
|
|
|
|
static LPCTSTR lpstrYesNo[]={ _T("Yes"), _T("No"), NULL };
|
|
|
|
//
|
|
// Read in one of two predefined comma delimted files, and display their contents in the
|
|
// multiline edit control in this dialog.
|
|
// Note that the input files for this example use the technique of putting two
|
|
// separate tables into a single CSV file, which is not supported by all programs. But,
|
|
// it can still be a very useful technique for your own use.
|
|
//
|
|
void CCsvTestDlg::OnOK()
|
|
{
|
|
//
|
|
// Retrieve the settings from the dialog
|
|
//
|
|
UpdateData();
|
|
|
|
COXCsvFile inFile;
|
|
CFileException fe;
|
|
LPCTSTR lpstrInFilename=(m_bUseHeaders ? _T("headers.csv") :
|
|
_T("noheader.csv"));
|
|
SData data;
|
|
|
|
//
|
|
// clear out the old data array
|
|
//
|
|
m_aData.RemoveAll();
|
|
|
|
//
|
|
// disable the save button
|
|
//
|
|
m_ctrlSave.EnableWindow(FALSE);
|
|
|
|
//
|
|
// Open the input file
|
|
//
|
|
if (!inFile.Open(lpstrInFilename, CFile::modeRead, &fe))
|
|
{
|
|
return ;
|
|
}
|
|
|
|
TRY
|
|
{
|
|
CWaitCursor cursor;
|
|
short nId;
|
|
int nYesNo;
|
|
int index;
|
|
|
|
OXTRACE(_T("CCsvTestDlg::OnOK()"));
|
|
|
|
//
|
|
// check if we shuold check for headers in the input file
|
|
//
|
|
if (m_bUseHeaders)
|
|
{
|
|
//
|
|
// Read in the headers from the input file. After reading the
|
|
// headers in from the first line of the file, set the aliases
|
|
// for the "Integer" column.
|
|
// Note: by using the column headers, and using those headers
|
|
// in the calls to ReadColumn(), the exact order of the columns
|
|
// in the CSV file becomes irrelevant to your program. This is
|
|
// shown by the fact that the columns in "headers.csv" are in
|
|
// a different order from "noheader.csv", but the results displayed
|
|
// in the edit control are the same.
|
|
//
|
|
inFile.GetColumns(8);
|
|
inFile.SetAliases(headers[3], aliases);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Since there are no headers in the input file, set the names
|
|
// of the columns that we will use in later calls, and let
|
|
// the COXCsvFile object know how many columns to expect.
|
|
// Note, if you want to only refer to the columns by their column
|
|
// indicies, this call does not need to be made, as the first call
|
|
// to ReadLine() will set the number of columns in the table.
|
|
//
|
|
inFile.SetColumns(headers);
|
|
}
|
|
|
|
//
|
|
// Read the individual records from one file to the other.
|
|
//
|
|
// NOTE : I said records, not lines, since quoted strings can
|
|
// contain new lines in them. This is a feature supported by programs
|
|
// like MS Access, but not by MS Excel.
|
|
//
|
|
while (inFile.ReadLine())
|
|
{
|
|
data.Clear();
|
|
|
|
if (inFile.IsLineEmpty())
|
|
{
|
|
//
|
|
// Blank lines can either be ignored, or they can be used like
|
|
// here to mark the end of one table, and the start of another
|
|
// one.
|
|
//
|
|
OXTRACE(_T("Reached the end of the first table"));
|
|
break;
|
|
}
|
|
|
|
OXTRACE(_T("Reading next line"));
|
|
|
|
//
|
|
// Read the data from the various columns into the members of the
|
|
// SData structure.
|
|
//
|
|
inFile.ReadColumn(_T("ID"), data.nId);
|
|
OXTRACE_WRITEVAL(_T("ID"), data.nId);
|
|
|
|
inFile.ReadColumn(_T("Name"), data.strName);
|
|
OXTRACE_WRITEVAL(_T("Name"), data.strName);
|
|
|
|
inFile.ReadColumn(_T("Byte"), data.ucByte);
|
|
OXTRACE_WRITEVAL(_T("Byte"), data.ucByte);
|
|
|
|
inFile.ReadColumn(_T("Integer"), data.nInt);
|
|
OXTRACE_WRITEVAL(_T("Integer"), data.nInt);
|
|
|
|
inFile.ReadColumn(_T("Float"), data.fFloat);
|
|
OXTRACE_WRITEVAL(_T("Float"), data.fFloat);
|
|
|
|
inFile.ReadColumn(_T("Double"), data.fDouble);
|
|
OXTRACE_WRITEVAL(_T("Double"), data.fDouble);
|
|
|
|
inFile.ReadColumn(_T("String"), data.strString);
|
|
OXTRACE_WRITEVAL(_T("String"), data.strString);
|
|
|
|
inFile.ReadColumn(_T("Valid"), nYesNo, lpstrYesNo);
|
|
OXTRACE_WRITEVAL(_T("Valid"), nYesNo);
|
|
data.bValid = (nYesNo == 0);
|
|
|
|
m_aData.Add(data);
|
|
}
|
|
|
|
//
|
|
// Read in the second table, merging its data with the first
|
|
//
|
|
if (m_bUseHeaders)
|
|
{
|
|
//
|
|
// Read in the headers for the second table in this file.
|
|
//
|
|
inFile.GetColumns(3);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the names, and number of columns to expect, for the
|
|
// second table in this file
|
|
//
|
|
inFile.SetColumns(headers2);
|
|
}
|
|
|
|
//
|
|
// Read the records in one at a time from the second table.
|
|
//
|
|
while (inFile.ReadLine())
|
|
{
|
|
if (inFile.IsLineEmpty())
|
|
{
|
|
//
|
|
// Blank lines can either be ignored, like here, or they can be
|
|
// used to mark the end of one table, and the start of another
|
|
// one.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
OXTRACE(_T("Reading next line"));
|
|
|
|
//
|
|
// Read the ID field for this record, and search for it in the
|
|
// SData array, to match the records from the two tables up to
|
|
// each other.
|
|
//
|
|
inFile.ReadColumn(_T("ID"), nId);
|
|
OXTRACE_WRITEVAL(_T("ID"), nId);
|
|
|
|
|
|
for (index = 0 ; index < m_aData.GetSize() ; ++index)
|
|
{
|
|
if (m_aData[index].nId == nId)
|
|
{
|
|
//
|
|
// found the matching record from the previous table
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index >= m_aData.GetSize())
|
|
{
|
|
//
|
|
// skip this record, as this ID did not exist in the other table
|
|
//
|
|
OXTRACE_WRITEVAL(_T("ID not found from earlier table"), nId);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Read the remaining columns into the SData structure from the previous
|
|
// table
|
|
//
|
|
inFile.ReadColumn(_T("Last Name"), m_aData[index].strLastName);
|
|
OXTRACE_WRITEVAL(_T("Last Name"), m_aData[index].strLastName);
|
|
|
|
inFile.ReadColumn(_T("Address"), m_aData[index].strAddress);
|
|
OXTRACE_WRITEVAL(_T("Address"), m_aData[index].strAddress);
|
|
}
|
|
|
|
//
|
|
// format the data for the multiline edit control
|
|
//
|
|
m_strData.Empty();
|
|
for (index = 0 ; index < m_aData.GetSize() ; ++index)
|
|
{
|
|
CString strTemp;
|
|
data = m_aData[index];
|
|
|
|
strTemp.Format(_T("%u. %s %s, %s\r\n")
|
|
_T(" %u, %d, %f, %f, %s\r\n")
|
|
_T(" \"%s\"\r\n"),
|
|
data.nId, data.strName, data.strLastName, data.strAddress,
|
|
data.ucByte, data.nInt, data.fFloat, data.fDouble,
|
|
(data.bValid ? _T("Valid") : _T("Invalid")),
|
|
data.strString);
|
|
m_strData += strTemp;
|
|
}
|
|
UpdateData(FALSE);
|
|
|
|
//
|
|
// enable the Save button
|
|
//
|
|
m_ctrlSave.EnableWindow(m_aData.GetSize() > 0);
|
|
}
|
|
CATCH_ALL(e)
|
|
{
|
|
inFile.Abort();
|
|
}
|
|
END_CATCH_ALL
|
|
}
|
|
|
|
//
|
|
// Write the data from the data array out to a CSV file. The data
|
|
// will be written out using the same two tables to a single CSV file
|
|
// format that it was read in with.
|
|
//
|
|
void CCsvTestDlg::OnSave()
|
|
{
|
|
//
|
|
// Update the members of the class from the dialog
|
|
//
|
|
UpdateData();
|
|
|
|
COXCsvFile inFile, outFile;
|
|
CFileException fe;
|
|
LPCTSTR lpstrOutFilename = _T("out.csv");
|
|
SData data;
|
|
|
|
//
|
|
// Open the output file
|
|
//
|
|
if (!outFile.Open(lpstrOutFilename, CFile::modeCreate | CFile::modeReadWrite, &fe))
|
|
{
|
|
return ;
|
|
}
|
|
|
|
TRY
|
|
{
|
|
CWaitCursor cursor;
|
|
|
|
OXTRACE(_T("CCsvTestDlg::OnOK()"));
|
|
|
|
//
|
|
// Check for header usage
|
|
//
|
|
if (m_bUseHeaders)
|
|
{
|
|
//
|
|
// Write out the headers for the first table
|
|
//
|
|
outFile.SetColumns(headers);
|
|
outFile.WriteHeaders();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set up how many columns there will be in the first table
|
|
//
|
|
outFile.Initialize(nColumns);
|
|
}
|
|
|
|
//
|
|
// loop through the SData elements that were filled in during the read.
|
|
//
|
|
int index = 0 ;
|
|
for (index = 0 ; index < m_aData.GetSize() ; ++index)
|
|
{
|
|
data = m_aData[index];
|
|
|
|
//
|
|
// Write the column data out to the current record buffer in outFile.
|
|
//
|
|
outFile.WriteColumn(0, data.nId);
|
|
outFile.WriteColumn(1, data.strName);
|
|
outFile.WriteColumn(2, data.ucByte);
|
|
outFile.WriteColumn(3, data.nInt);
|
|
outFile.WriteColumn(4, data.fFloat);
|
|
outFile.WriteColumn(5, data.fDouble);
|
|
outFile.WriteColumn(6, data.strString);
|
|
outFile.WriteColumn(7, lpstrYesNo[data.bValid ? 0 : 1]);
|
|
|
|
//
|
|
// Write this record out to the output file.
|
|
//
|
|
outFile.WriteLine();
|
|
}
|
|
|
|
//
|
|
// This call to WriteLine, with no columns, will write out a blank line,
|
|
// providing a separator between the tables.
|
|
// Initialize is called to clear the old header information.
|
|
//
|
|
outFile.Initialize();
|
|
outFile.WriteLine();
|
|
|
|
if (m_bUseHeaders)
|
|
{
|
|
//
|
|
// Write out the headers for the second table in the file.
|
|
//
|
|
outFile.SetColumns(headers2);
|
|
outFile.WriteHeaders();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set up how many columns there will be in the second
|
|
// table in the file.
|
|
//
|
|
outFile.Initialize(3);
|
|
}
|
|
|
|
//
|
|
// Loop through the records one more time.
|
|
//
|
|
for (index = 0 ; index < m_aData.GetSize() ; ++index)
|
|
{
|
|
data = m_aData[index];
|
|
|
|
//
|
|
// Write the data for this record to the current record
|
|
// buffer in outFile.
|
|
//
|
|
outFile.WriteColumn(0, data.nId);
|
|
outFile.WriteColumn(1, data.strLastName);
|
|
outFile.WriteColumn(2, data.strAddress);
|
|
|
|
//
|
|
// Write the current record to the output file.
|
|
//
|
|
outFile.WriteLine();
|
|
}
|
|
}
|
|
CATCH_ALL(e)
|
|
{
|
|
outFile.Abort();
|
|
}
|
|
END_CATCH_ALL
|
|
}
|
|
|
|
void CCsvTestDlg::OnAbout()
|
|
{
|
|
// TODO: Add your control notification handler code here
|
|
|
|
CUTSampleAboutDlg dlgAbout(IDR_MAINFRAME,ID_DESCRIPTION_FILE);
|
|
dlgAbout.DoModal();
|
|
}
|