It is now February 12
following a considerable delay to do my taxes and I have just succeeded in
displaying a button on the window of the previous post using non-visual C++.
After displaying the
window of the previous post, prior to my timeout, I had numerous unsuccessful
tries attempting to display a control on the window. Yet all I could manage were child windows that did not display in
a satisfactory manner.
The first attempt
just displayed the upper-right set of minimize, maximize, and terminate buttons
that had strange behaviors when used but could be recovered by hovering where
they should be. Or at least sort of
recovered. The second attempt produced
a child window that used all the space within the parent, main window.
After playing with
the second child window for a while I gave up and did my taxes. This morning I returned to my attempt to
display a control within the main window and in about an hour was able to do
so.
I first found an
example for doing a MessageBox and added it which immediately worked as it
should. By luck, I suppose, I added the
code for it immediately after creating the main window (after removing the
previous code that created the child window within the main window) and before
invoking the ShowWindow and UpdateWindow functions that cause the main window
to be displayed.
So there was the
popup for the MessageBox and after clicking on its OK button the ShowWindow and
UpdateWindow fuctions were invoked and the main window was displayed as in the
previous post.
This was encouraging
but it wasn't what I was looking for since I wanted to display window controls
within the main window. So I again did
a search to display a button via C++ without using the visual compiler. And this time I struck pay dirt immediately.
Again the example
showed creating a child window. The
kind of search results that I had discovered before that had just produced an
actual window (or the rudiments of a window) when I had tried before. Only this time it worked.
The search example
was
HWND
hwndButton = CreateWindow(
L"BUTTON", // Predefined
class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD |
BS_DEFPUSHBUTTON, // Styles
10, // x position
10, // y position
100, // Button width
100, // Button height
m_hwnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLong(m_hwnd,
GWL_HINSTANCE),
NULL); // Pointer not needed.
which was quite
similar to what I had found before.
But, because of my experience with the MessageBox, I added the code
immediately after the creation of the main window.
Using the GNAT GPS
compiler the use of the L constructs prior to the "BUTTON" and
"OK" text wouldn't compile but removing the L's while using my
reference to the parent window did compile as in
HWND hwndButton = CreateWindow
(
"BUTTON", // Predefined
class; Unicode assumed
"OK", // Button text
WS_TABSTOP |
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
10, // x position
10, // y position
100, // Button width
100,
// Button height
hMainWnd, // Parent window
NULL, // no menu
(HINSTANCE)GetWindowLong(hMainWnd, GWL_HINSTANCE),
NULL ); //
Pointer not needed.
And, voila!, the
window displayed with the button as below when I ran the application.
So now I was finally able to continue with finishing up the
Learn to Code GR application in non-visual C++ using a window to obtain the file
to be parsed and displaying the results.
Then in the afternoon I found out how to detect the click
event and how to associate it with a particular control. That is, in the callback the uMsg message
identifier is 273 indicating WM_COMMAND (where WM_NOTIFY is supposed to another
possibility) and the lParam turns out to be the handle of the button. Therefore, the value of lParam can be
compared to a table of created control handles to determine which was clicked.
Note: The callback for the main window is
LRESULT CALLBACK MainWndProc( HWND hMainWnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam ) // second message parameter
so the uMsg parameter is to be checked for WM_COMMAND and,
if it is, the lParam is to be checked to determine if it is the handle of the
particular control.
To do this I added an array of a struct containing the created
control handles along with a supplied name – in this instance "Select
File". Therefore, when the
callback receives a WM_COMMAND the array can be searched for the handle and,
upon a match, obtain the name supplied for the control to check if the control
of the event is the one wanted. That
is,
case
WM_COMMAND:
{
for
(int c = 0; c < numberOfControls; c++)
{ if
(control[c].handle == (HWND)lParam) // found the control
{
std::string name = control[c].name;
if (name
== "Select File")
{
if (!fileSelected)
{
fileSelected = fileLocate();
return TRUE;
}
}
}
} //
end for loop
return
0;
}
As I knew must be the case and which brought to my attention
later when I invoked the parse of the Learn to Code GR file from within the
callback, the callback runs in a different thread from the WinMain
application. This is because it is an
extension of the Windows operating system passing Windows events to the
application. Therefore, longer running
code needs to be done in another thread.
Not that the read of the disk file and the parse of its records takes
very long (since the particular file is small) but because it just doesn't belong
in the op sys thread and because the callback never returns to the WinMain
application thread.
I couldn't find that the GNAT C++ had the OpenFileDialog()
function to allow the user to select the file to be parsed. Since to make my own function would be a
significant task – especially while trying to find the windows functions needed
for non-visual C++ – I decided to have an edit box as a control into which the
user could enter the path. Perhaps I
can do my own version of the function later.
In one day I discovered how to do the code to have bounded
and unbounded (what I got to start as the default) edit boxes that I could use
to display instructions, have the user fill in as the file path, and announce
an error if the entered text didn't result in the file being found to
open. And to clear the error when the
correct path was entered.
To do this I had to move the button and change its meaning
since ENTER wasn't accepted to terminate the entry of the path. Therefore, I used the click on the button to
indicate that the path had been entered.
I also added an error label (an unbounded edit box) for when
the entered path was invalid and a set of labels and text boxes to report the
results of parsing the file (assuming the entered file is in the expected format). These extra edit boxes I made visible only
when they were needed. This is
automatic with the error box since it doesn't have a boundary and hence nothing
is visible when there isn't any text being displayed.
So after failing to find a way to display controls within
the window and only getting funky child windows for over a week, after the wait
it all came together in 3 days.
The window when an error is displayed appears as
The window after the Learn to Code GR portion of the
application has run is
Notice how the labels (the edit boxes without the boundary
lines) get clipped along their bottom edge for some reason whereas they are
displayed initially with the letters fully formed as can be seen in the first of
these three captured windows. Perhaps
this is because the edit boxes with borders have a height of 30 units whereas
those without have a height of 15. As
can be seen from the first screen this is sufficient to display the text but
perhaps iffy as window updates occur.
(In cleaning up the code to display it below I changed the height of the
labels to 20 and the text is no longer clipped along the bottom edge.)
The regular upper right X button could, of course, be used
to terminate the application. The Done
button just calls attention to the ability to terminate.
The non-visual C++ code that creates the window and its
controls is as follows.
#ifndef KEYTABLECPP
#define KEYTABLECPP
// Key class
class Key
{
public:
Key(); //
constructor
int
key; // value of key
int
valueCount; // number of different values associated with key
static
const int VALUESSIZE = 20; // initial size of values & sums
// arrays for particular key
int
*values; // will point to the array
int
*sums; // "
}; // end class Key
// KeyTable class
class KeyTable //: public Component
{
public:
KeyTable(); // constructor
int
getKey(int index);
int
getKeyCount();
// Return
number of different values for key at index
int
getValueCount(int index);
int
setKey(int key);
int
setValue(int index, int value);
int
getValue(int indexKey, int indexValue);
int
incSum(int indexKey, int indexValue);
int
getSum(int indexKey, int indexValue);
private:
int
keyCount; // number of different keys
static
const int TABLESIZE = 30; // initial size of keys array
Key* keys
= new Key[TABLESIZE]; // instances of
"struct" class
}; // end class KeyTable
#endif // KEYTABLECPP
Key and KeyTable class within KeyTable.cpp
#include <iostream>
#include "keytable.h"
Key::Key() // constructor
{
valueCount
= 0;
key = 0;
values =
new int[VALUESSIZE];
sums = new
int[VALUESSIZE];
for (int i
= 0; i < VALUESSIZE; i++)
{
values[i]
= -1; // so no match until a value added
sums[i] =
0; // so any first increment of the
sum will be to 1
}
} // end Key constructor
KeyTable::KeyTable() // constructor
{
this->keyCount = 0;
} // end KeyTable constructor
// Return key at index
KeyTable::getKey(int index)
{
int
tempKey;
if (index
< KeyTable::keyCount)
{
int key =
KeyTable::keys[index].key;
return
key;
}
else
{
return 0;
}
} // end getKey
// Return number of keys in table
KeyTable::getKeyCount()
{
return
keyCount;
} // end getKeyCount
KeyTable::getValueCount(int index)
{
return
this->keys[index].valueCount;
} // end getValueCount
KeyTable::setKey(int key)
{
for (int i
= 0; i < keyCount; i++)
{
if (this->keys[i].key
== key) // key already in table
{
return
i;
}
} // end
for loop
if
(this->keyCount < TABLESIZE) // add key to table if room
{
this->keys[keyCount].key = key;
int index
= keyCount;
keyCount++;
return
index;
}
else
{
std::cout
<< "Error: Too many different keys" << "\n";
return
-1;
}
} // end setKey
// Add value for key at index, return value index
KeyTable::setValue(int index, int value)
{
for (int i
= 0; i < this->keys[index].valueCount; i++)
{
if (
this->keys[index].values[i] == value )
{
//
Increment sum
this->keys[index].sums[i]++;
return
i;
}
} // end
for loop
// Add
value to array for key since didn't return for existing value
this->keys[index].values[this->keys[index].valueCount] = value;
this->keys[index].sums[this->keys[index].valueCount]++;
int
valueIndex = this->keys[index].valueCount;
this->keys[index].valueCount++; // increment number of values for key
return
valueIndex;
} // end setValue
// Get value for key at indexes, return value
KeyTable::getValue(int indexKey, int indexValue)
{
return
this->keys[indexKey].values[indexValue];
} // end getValue
// Increment Sum for value of key at indexKey and
value at indexValue
KeyTable::incSum(int indexKey, int indexValue)
{
//
Increment sum
int count =
this->keys[indexKey].sums[indexValue]++;
return
count;
} // end incSum
// Get sum for key at indexes, return sum
KeyTable::getSum(int indexKey, int indexValue)
{
return
this->keys[indexKey].sums[indexValue];
} // end getSum
WinMain.cpp
// from
https://msdn.mircosoft.com/en-us/library/windows/desktop/ms633575(v=vs.85).aspx
// as modified for the WinMain portion of this file.
#include <stdio.h>
#include <iostream>
#include <cstring>
#include "windows.h"
#include "wingdi.h"
#include "winuser.h"
#include "afxres.h"
#include <keytable.h>
// Global variables
HINSTANCE hinst;
HWND hMainWnd;
ATOM mainWindow;
int ncmdShow;
// Structure to keep track of controls created for
the window
int numberOfControls = 0; // number of controls
created
struct Controls {
std::string
name; // name of control
HWND
handle; // handle of control
};
Controls control[10];
// Other variables used by both the app thread and
the callback thread
BOOL fileSelected = FALSE; // file to parse selected
BOOL fileOpenEvent = TRUE; // only run the Parse,
etc once
FILE* selectedFile;
BOOL finished = FALSE; // finished looking at
window, time to exit
// Function prototypes.
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR,
int);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
BOOL StartApp();
// Note: MainWndProc runs in a Windows OpSys thread
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM,
LPARAM);
// as
does fileLocate since called from MainWndProc
BOOL fileLocate(HWND editWnd); // Find file to be
used
// Application entry point.
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow )
{
MSG msg;
ncmdShow =
nCmdShow;
// Create
window and the initial controls
BOOL
result;
result =
InitApplication(hinstance);
if
(!result)
return
FALSE;
if
(!InitInstance(hinstance, nCmdShow))
return
FALSE;
// Main
"forever" loop
BOOL
fGotMessage;
while
((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0 &&
fGotMessage != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if
((fileSelected) & (fileOpenEvent))
{
fileOpenEvent = FALSE; // dummy indication that can only be used once
//
Start main portion of the application.
result
= StartApp();
}
if
(finished)
{
break;
// exit while loop
}
} // end
while loop
// Exit the
application
return
msg.wParam;
UNREFERENCED_PARAMETER(lpCmdLine);
} // end WinMain
// To treat Windows events while executing in a
separate thread
LRESULT CALLBACK MainWndProc( HWND hMainWnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam ) // second message parameter
{
PAINTSTRUCT
ps;
HDC hdc;
switch
(uMsg)
{
case
WM_CREATE:
//
Initialize the window.
return
0;
case
WM_PAINT:
{
//
Paint the window's client area.
return
0;
}
case
WM_SIZE:
// Set
the size and position of the window.
return
0;
case
WM_DESTROY:
//
Clean up window-specific data objects.
return
0;
case
WM_DRAWITEM:
{
return
0;
}
case
WM_SETTEXT:
{
return
true;
}
//
WM_COMMAND (control events) and WM_NOTIFY
case
WM_COMMAND:
{ //
Output to console indicating when control event occurs
std::string report = ("WM_COMMAND ");
report
= report + std::to_string(uMsg);
report
= report + " wParam " + std::to_string(wParam);
report
= report + " lParam " + std::to_string(lParam);
std::cout << report << '\n';
// Find if control of interest used
HWND
errorHandle = 0;
for
(int c = 0; c < numberOfControls; c++)
{ if
(control[c].handle == (HWND)lParam) // found the control
{
std::string name = control[c].name;
if
(name == "Select File") // Select File button clicked
{
if (!fileSelected)
{
// Find the handle for the Enter File edit control
for (int f = 0; f < numberOfControls; f++)
{
if (control[f].name == "Enter File")
{
// Pass handle of edit control to read the entered path
fileSelected = fileLocate(control[f].handle);
for (int e = 0; e < numberOfControls; e++)
{
if (control[e].name == "Error Label")
{ // Set handle of error control to either report error or clear
errorHandle = control[e].handle;
exit;
}
}
if (!fileSelected)
{ // Output error to error label
SetWindowText( errorHandle,
(LPCTSTR)"Invalid File
Selected");
UpdateWindow(hMainWnd);
}
else
{ // Clear error label
SetWindowText( errorHandle,
(LPCTSTR)"");
UpdateWindow(hMainWnd);
}
return fileSelected; //
remember that these returns are to Windows
}
}
return TRUE;
}
}
// end Select File
if
(name == "Enter File")
{
std::string comment = name;
std::cout << comment << '\n';
}
if
(name == "Done")
{
finished = TRUE; // set for use by WinMain thread
return TRUE;
}
}
} //
end for loop
return 0;
}
case
WM_NOTIFY:
{
std::string report = ("WM_NOTIFY ");
report
= report + std::to_string(uMsg);
std::cout << report << '\n';
return
TRUE;
}
//
Process other messages.
default:
return
DefWindowProc(hMainWnd, uMsg, wParam, lParam);
}
return 0;
} // end MainWndProc
// Register the main window class
BOOL InitApplication(HINSTANCE hinstance)
{
WNDCLASSEX
wcx;
// Fill in
the window class structure with parameters
// that
describe the main window.
wcx.cbSize
= sizeof(wcx); // size of
structure
wcx.style =
CS_HREDRAW | CS_VREDRAW; // redraw
if size changes
wcx.lpfnWndProc = (WNDPROC) MainWndProc; // points to window procedure
wcx.cbClsExtra = 0;
// no extra class memory
wcx.cbWndExtra = 0;
// no extra window memory
wcx.hInstance = hinstance;
// handle to instance
wcx.hIcon =
LoadIcon(NULL, IDI_APPLICATION); //
predefined app. icon
wcx.hCursor
= LoadCursor(NULL, IDC_ARROW); //
predefined arrow
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // white background
brush
wcx.lpszMenuName =
"MainMenu"; // name
of menu resource
wcx.lpszClassName = "MainWClass"; // name of window class
wcx.hIconSm
= (HICON)LoadImage( hinstance, // small class icon
MAKEINTRESOURCE(5),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_DEFAULTCOLOR );
// Register
the window class.
mainWindow
= RegisterClassEx(&wcx);
DWORD y;
if
(mainWindow==0)
{ y =
GetLastError();
}
// Return
RegisterClassEx(&wcx);
return
(BOOL)mainWindow;
} // end InitApplication
// Create the main window and the initial controls.
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
// Save the
application-instance handle.
hinst =
hinstance;
// Create
the main window.
hMainWnd =
CreateWindow
( "MainWClass",
// name of window class
"LearnToCodeGR", //
title-bar string
WS_OVERLAPPEDWINDOW, // top-level window
CW_USEDEFAULT, // default
horizontal position
CW_USEDEFAULT, // default
vertical position
CW_USEDEFAULT, // default
width
CW_USEDEFAULT, // default
height
(HWND) NULL, // no owner
window
(HMENU) NULL, // use class
menu
hinstance, // handle to
application instance
(LPVOID) NULL ); // no
window-creation data
if
(!hMainWnd)
return
FALSE;
if
(hMainWnd!=0)
{
// Create
Select File button control.
HWND hwndSelButton
= CreateWindow
( "BUTTON", // Predefined class; Unicode assumed
"Select File", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD |
BS_DEFPUSHBUTTON, // Styles
425, // x position
38, // y position
100, // Button width
35, // Button height
hMainWnd, // Parent window
NULL, // no menu
(HINSTANCE)GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // Pointer not needed.
control[numberOfControls].handle = hwndSelButton; // Capture handle
control[numberOfControls].name = "Select File"; //
and name of the
numberOfControls++; //
button control
// Create
label with instruction to enter the path of the file to be parsed.
HWND
hLabelWnd = CreateWindow
( "EDIT", //
predefined class
"Enter file path and then click Select
File", // label text
WS_CHILD | WS_VISIBLE | ES_LEFT, // Styles
10, 10, 310, 20, // set size and location
hMainWnd, // parent window
NULL, //(HMENU) ID_EDITCHILD, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
// Create
edit box in which the path is to be entered.
HWND
hEditWnd = CreateWindow
( "EDIT", //
predefined class
"", // no initial edit
text
WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
10, 40, 410, 30, // set size in WM_SIZE message
hMainWnd, // parent window
NULL,
// edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
control[numberOfControls].handle = hEditWnd; // Capture handle
control[numberOfControls].name = "Enter File"; // and name of the
numberOfControls++; //
button control
// Create
label to be used to display an error for invalid file path.
HWND
hErrorWnd = CreateWindow
( "EDIT", // predefined class
" ", // no initial label text
WS_CHILD | WS_VISIBLE | ES_LEFT, // Styles
10, 80, 310, 20, // set size and location
hMainWnd, // parent window
NULL, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
control[numberOfControls].handle = hErrorWnd; // Capture handle
control[numberOfControls].name = "Error Label"; // and name of the
numberOfControls++; //
error control
// Create
button to use to exit the application.
HWND hwndDoneButton
= CreateWindow
( "BUTTON", // Predefined class; Unicode assumed
"Done", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD |
BS_DEFPUSHBUTTON, // Styles
10, // x position
310, // y position
100, // Button width
35, // Button height
hMainWnd, //
Parent window
NULL, // no menu
(HINSTANCE)GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // Pointer not needed.
control[numberOfControls].handle = hwndDoneButton; // Capture handle
control[numberOfControls].name = "Done"; //
and name of the
numberOfControls++; //
button control
// Create
OK Popup with meaningless title and text.
// int
msgBox;
// msgBox =
MessageBox(hMainWnd, "abc", "cap", 0);
} // end if
for main window created
// Show the
main window and send a WM_PAINT message to the window procedure.
ShowWindow(hMainWnd, nCmdShow);
UpdateWindow(hMainWnd);
return
TRUE;
}
// Get the text entered into the edit box and
attempt to open the entered path.
// Note: This function is called from the callback
so runs in a Windows thread.
BOOL fileLocate(HWND editWnd)
{
// Read the
text from the edit control.
char
path[101];
for (int i
= 0; i < 100; i++) // clear the path array
{
path[i] =
char(0);
}
int count =
GetWindowText
( editWnd, // edit control
handle
(LPSTR)path, // string read from control
100 ); // maximum number of
characters to read
// Attempt
to open the file.
if (count
> 0)
{
path[count] = char(0);
selectedFile = fopen(path, "r");
if
(selectedFile == NULL)
{
return
FALSE;
}
}
else
{
return
FALSE;
}
return
TRUE; // indicate that the file was opened
} // end fileLocate
/*
* From here
to the end is the C++ code for the LearnToCodeGR application.
* This code
runs in the WinMain application thread.
*/
KeyTable keyTable; // Instance of KeyTable class
object
// Report the key and value with the most references
void Report()
{
// Report
individual results to the console.
printf("\n");
printf("Report ");
printf("\n");
for (int i
= 0; i < keyTable.getKeyCount(); i++)
{
for (int
j = 0; j < keyTable.getValueCount(i); j++)
{
std::string report = ("Key ");
report
= report + std::to_string(keyTable.getKey(i));
report
= report + (" Value ");
report
= report + std::to_string(keyTable.getValue(i,j));
report = report + (" Sum ");
report
= report + std::to_string(keyTable.getSum(i,j));
std::cout << report << '\n';
} // end
for loop
} // end
for loop
// Items
identifying the key with the largest number of a particular value.
int key =
0;
int value =
0;
int sum =
0;
for (int i
= 0; i < keyTable.getKeyCount(); i++)
{
for (int
j = 0; j < keyTable.getValueCount(i); j++)
{
if
(keyTable.getSum(i,j) > sum) // save key and value with greater sum
{
key =
keyTable.getKey(i);
value
= keyTable.getValue(i,j);
sum =
keyTable.getSum(i,j);
}
} // end
for loop
} // end
for loop
// Report
the key and value with the greatest number of references to the console.
printf("\n");
std::string
report = ("Key and Value with greatest Sum ");
report =
report + std::to_string(key) + " ";
report =
report + std::to_string(value) + " ";
report =
report + std::to_string(sum);
std::cout
<< report << '\n';
// Create
and report the results to window controls.
// Convert
int's to char arrays.
std::string
keyStr = std::to_string(key);
int size;
size =
keyStr.size();
char
*keyVal = new char[size+1];
keyVal[size] = 0;
memcpy(keyVal, keyStr.c_str(), size);
std::string
valueStr = std::to_string(value);
size =
valueStr.size();
char
*valueVal = new char[size+1];
valueVal[size] = 0;
memcpy(valueVal, valueStr.c_str(), size);
std::string
sumStr = std::to_string(sum);
size =
sumStr.size();
char
*sumVal = new char[size+1];
sumVal[size] = 0;
memcpy(sumVal, sumStr.c_str(), size);
HWND
hResultsWnd = CreateWindow
( "EDIT", //
predefined class
"Key and Value with greatest Sum",
// label text
WS_CHILD | WS_VISIBLE | ES_LEFT,
// Styles
10, 150, 300, 20, // set size and location
hMainWnd, // parent window
NULL, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
HWND
hKeyLabelWnd = CreateWindow
( "EDIT", // predefined class
"Key ", // label text
WS_CHILD | WS_VISIBLE | ES_LEFT, // Styles
10, 180, 60, 20, // set size and location
hMainWnd, // parent window
NULL, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
HWND
hKeyValueWnd = CreateWindow
( "EDIT", // predefined class
(LPCTSTR)keyVal, // result
for key
WS_CHILD | WS_VISIBLE | ES_LEFT |
WS_BORDER,
60, 178, 60, 30, // set size in WM_SIZE
message
hMainWnd, // parent window
NULL, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
HWND
hValLabelWnd = CreateWindow
( "EDIT", // predefined
class
"Value", // label text
WS_CHILD | WS_VISIBLE | ES_LEFT, // Styles
10, 220, 60, 20, // set size and location
hMainWnd, // parent window
NULL, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
HWND
hValValueWnd = CreateWindow
( "EDIT", // predefined class
(LPCTSTR)valueVal,// result for key
WS_CHILD | WS_VISIBLE | ES_LEFT |
WS_BORDER,
60, 218, 60, 30, // set size in WM_SIZE message
hMainWnd, // parent window
NULL, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
HWND
hSumLabelWnd = CreateWindow
( "EDIT", // predefined class
"Sum ", // label text
WS_CHILD | WS_VISIBLE | ES_LEFT, // Styles
10, 260, 60, 20, // set size and location
hMainWnd, // parent window
NULL, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd,
GWL_HINSTANCE),
NULL ); // pointer not needed
HWND
hSumValueWnd = CreateWindow
( "EDIT", // predefined class
(LPCTSTR)sumVal, // result for key
WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
60, 258, 60, 30, // set size in WM_SIZE
message
hMainWnd, // parent window
NULL, // edit control ID
(HINSTANCE) GetWindowLong(hMainWnd, GWL_HINSTANCE),
NULL ); // pointer not needed
UpdateWindow(hMainWnd); // cause the controls to be displayed in the
main window
return;
} // end Report
// Update keyTable with data from a parsed line
void Update(int nKey, int nValue1, int nValue2)
{
// This
function first checks if the key is new and, if so, adds it to the
// keys
array. It then does similar for the
values associated with it.
// Check
whether the key is already in the table
int
keyIndex = -1;
int
valueIndex = -1;
int
keyCount = keyTable.getKeyCount();
for (int i
= 0; i < keyCount; i++)
{
if
(keyTable.getKey(i) == nKey)
{
keyIndex = i; // key already in
table
break;
// exit loop
}
} // end
loop
if
(keyIndex < 0) // key not in the table
{
keyIndex
= keyTable.setKey(nKey); // add key
valueIndex = keyTable.setValue(keyIndex,nValue1); // add value for key
if
(nValue1 != nValue2)
{
keyTable.setValue(keyIndex,nValue2); }
else
{
keyTable.incSum(keyIndex,valueIndex); }
}
else // key
in the table
{
int
valueCount = keyTable.getValueCount(keyIndex);
valueIndex = keyTable.setValue(keyIndex,nValue1); // add value for key
if
(nValue1 != nValue2)
{
keyTable.setValue(keyIndex,nValue2); }
else
{
keyTable.incSum(keyIndex,valueIndex); }
}
return;
} // end Update
#define NINE 57 // ASCII character for digit 9
#define ZERO 48 // ASCII character for digit 0
// Convert character array to int
int toInt(char* data, int length) //iS, int iE)
{
int index =
length - 1; // loop in reverse
int digit;
int m =
1; // multiplier for shift
int number
= 0; // Numeric result
while
(index >= 0) //iS)
{
digit =
data[index] - ZERO; // convert ASCII to digit
number =
number + (m * digit);
m = m *
10;
index--;
}
return
number;
} // end toInt
enum Scan //Fields
{ BYPASS, FKEY, VALUE1, VALUE2 };
// Extract the fields of interest from the file's
line and retain the data
void Parse(int count, char* data)
{
#define CR
'\r' // carriage return
#define HT
'\t' // horizontal tab
#define LF
'\n' // line feed
#define
TRUE 1
// Parse
the supplied line of data from the file to obtain the three
// fields
of interest (key and two values), convert those fields to
//
integers, and then update a data structure (the KeyTable class) to
// retain
the data for evaluation when the complete file has been parsed.
int
keyValue = 0;
int
value1Value = 0;
int
value2Value = 0;
char
alphaDigits [10];
int numDigits
= 0;
bool
updateDone = false;
int offset
= 0; // offset into data
Scan
scanPhase = BYPASS;
// Parse
all bytes of record previously read from the file. Each field of
// interest
is preceded by a horizontal tab. The
end of the final field
// is
marked by a CR LF or only a LF (NL) except the last record where
// there
are no more characters to be treated.
while
(offset < count)
{
switch
(scanPhase)
{
case
BYPASS:
if
(data[offset] == HT)
{
scanPhase = FKEY;
keyValue = 0;
value1Value = 0;
value2Value = 0;
numDigits = 0;
}
break;
case
FKEY:
if
(data[offset] != HT)
{
if
((data[offset] >= ZERO) & (data[offset] <= NINE))
{
alphaDigits[numDigits] = data[offset];
numDigits++;
}
}
else
{
keyValue = toInt(alphaDigits, numDigits);
scanPhase = VALUE1;
numDigits = 0;
}
break;
case
VALUE1:
if
(data[offset] != HT)
{
if
((data[offset] >= ZERO) & (data[offset] <= NINE))
{
alphaDigits[numDigits] = data[offset];
numDigits++;
}
}
else
{
value1Value = toInt(alphaDigits, numDigits);
scanPhase = VALUE2;
numDigits = 0;
}
break;
case
VALUE2:
if
((data[offset] == LF) | (data[offset] == CR) | offset+1 >= count)
{
value2Value = toInt(alphaDigits, numDigits);
//
Update tables with the data from the record.
Update( keyValue, value1Value, value2Value );
updateDone = true;
//
Initialize for next record
keyValue = 0;
value1Value = 0;
value2Value = 0;
numDigits = 0;
scanPhase = BYPASS;
}
else
{
if
((data[offset] >= ZERO) & (data[offset] <= NINE))
{
alphaDigits[numDigits] = data[offset];
numDigits++;
}
}
break;
} // end
switch
//
Complete processing of final record when not terminated by New Line
offset++;
// increment to next data buffer position
if (offset
>= count) // no more data
{
if
(value2Value > 0) // last record fully parsed without trailing NL
{
if
(!updateDone)
{
value2Value = toInt(alphaDigits, numDigits);
Update( keyValue, value1Value, value2Value );
updateDone = true;
}
}
break;
// exit loop
}
} // end
loop
return;
} // end Parse
BOOL StartApp()
// Treat the opened the LearnToCodeGR file.
{
char
buffer[30];
int
currPos;
int lastPos
= 0;
int length;
// Read
each record/line of the file and Parse it.
while
(!feof(selectedFile))
{
fgets(buffer, 30, (FILE*)selectedFile);
currPos =
ftell(selectedFile);
length =
currPos - lastPos; // number of characters read into buffer
lastPos =
currPos;
for (int
i=0; i < length; i++) // output the
printf("%c ", buffer[i]);
// record to
printf("\n");
// the console
// Parse
the line and update to retain all the necessary data.
Parse(length, buffer);
}
// Close
the selected file.
fclose(selectedFile);
// Report
the results.
Report();
printf("return from Report()");
printf("\n");
return
TRUE;
} // end StartApp
No comments:
Post a Comment