Monday, February 19, 2018

Use of Windows Controls from C++, Part 3





Well that was easy.  I started mid morning to implement the use of the list box that I created yesterday and played around getting a dialog box to compile.  Then decided that a dialog box wasn't something I wanted to use and returned to what would be necessary to fill in the empty list box.  By 2:15 I had it filled in with all the files and folders in my starting folder with a vertical scroll bar replacing the control that had had only boundary lines.

And now, one and half days later it is all working.  It seems that I'm beginning to be quite at home at the online University of Microsoft.

The application detects when a folder/directory versus a file is highlighted and switches to displaying the files in the new folder when a folder is selected.  Otherwise, it opens the file as the file to parse.  So this is similar to the OpenFileDialog() that is an available function with the visual compilers.

I did have some problems.  I wanted to have the list box display in the order found when using the FindNextFile function but the files were in alpha order even thou LBS_SORT was not specified as a style and I couldn't find a way to avoid this so I had to give up.  The files were not in the order presented by Windows Explorer for any of the ways to change the order such as by name, by type, by creation date, etc.

Also, to empty the list box prior to displaying the files of a newly selected folder, the LB_RESETCONTENT message didn't work.  By that I mean didn't work well.  The list box just disappeared except initially a strange vague shape where its top should have been.  So I had to delete its contents line by line and this in reverse from last to first using the LB_DELETESTRING message to the list box control.

However, when one way wouldn't work I was able to find another without too much delay.

Jing captures of the window are shown below.  First how the window is displayed when the application is first launched.  Then, after the Select File button has been clicked.  Following that after the needed folder name has been selected and then after the file to be parsed has been selected.  Finally, how the sequence can be restarted by clicking the Select File button once again.

Following these graphics the code is presented.  The code to report the results, including the Key and KeyTable classes, is the same as the last post (except for truncating the initial label to avoid overlapping the list box) as well as the code to parse the file.  The code to open the file in the StartApp function is the same except for needing the obtainFilePath() function to obtain the path of the selected file.








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
#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; // the main window
ATOM mainWindow;

HWND hSelListBoxWnd = 0; // the listbox

int ncmdShow; // from lauch of the application

// 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];

// Structure to keep track of files and folders loaded into listbox
int listBoxItemsCount = 0; // largest index into the array
int nameMaxSize = 300;
struct ListBoxItems {
  DWORD fileAttributes; // attributes of the file or folder
  CHAR  fileName[300];  // name of the file or folder
};
int listBoxItemsMax = 500;
ListBoxItems listBoxItems[500];

int dirListBoxIndex = 0; // index to use to obtain directory for fileRelocate
std::string startDir;    // directory to use for list box

int fileListBoxIndex = 0; // index to use to obtain filename to be opened

// Other variables used by both the app thread and the callback thread
BOOL locateFile = FALSE;   // activate ListBox to select the file
BOOL relocateFile = FALSE; // clear ListBox to fill with files of new directory
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 fileLocate();      // Find file to be used using listbox
BOOL fileRelocate();    // Find file in new directory using listbox
BOOL emptyTheListBox(); // Empty the listbox
char* obtainFilePath(); // Form the filepath from selected filename

BOOL StartApp(); // Determine the LearnToCodeGR results

// Note: MainWndProc runs in a Windows OpSys thread
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);


// Application entry point.
int WINAPI WinMain( HINSTANCE hinstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine,
                    int nCmdShow )
{
  MSG msg; // message

  ncmdShow = nCmdShow;

  // Create main 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);

    // Set of possibilities to run in application thread initiated by setting variable
    // in callback thread.

    // Fill listbox using initial directory/folder
    if (locateFile)
    {
      fileLocate();
    }

    // Fill listbox after the selection of a new directory/folder
    if (dirListBoxIndex > 0)
    {
      fileRelocate();
    }

    // Parse selected file and display results
    if ((fileSelected) & (fileOpenEvent)) // open selected file
    {
      fileOpenEvent = FALSE; // dummy indication that can only be used once
      // Start main portion of the application.
      result = StartApp();
    }

    // Exit the forever loop to exit the application
    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;
    }

    case WM_INITDIALOG:
    {
      return 0;
    }

    // WM_COMMAND (treat control events)

    case WM_COMMAND:
    {
      // Indicate when control event of interest occurs
      BOOL matchedHandle = FALSE;

      // 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
        {
          matchedHandle = TRUE;

          std::string name = control[c].name;
          if (name == "Select ListBox") // Select from ListBox clicked
          {
            switch (LOWORD(wParam))
            {
              case IDOK:
              case IDCANCEL:
                EndDialog(control[c].handle, LOWORD(wParam));
                return TRUE;
            }
            switch (HIWORD(wParam))
            {
              case LBN_SELCHANGE: // listbox selection made
              {
                // Get selected index.
                int lbItem = (int)SendMessage(control[c].handle, LB_GETCURSEL, 0, 0);

                // Get listbox item data.
                int index = (int)SendMessage(control[c].handle, LB_GETITEMDATA,
                                             lbItem, 0);

                // Determine if the selected listbox item is a directory/folder
                if ((listBoxItems[index].fileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                    != 0)
                {
                  dirListBoxIndex = index;
                }
                else
                {
                  fileListBoxIndex = index;
                  fileSelected = TRUE; // indicate that file should be opened
                }

                return TRUE;

              }
            }
          }
          else if (name == "Select File") // Select File button clicked
          {
            locateFile = TRUE; // indicate that ListBox should be activated
          } // end Select File

          if (name == "Done")
          {
            finished = TRUE; // set for use by WinMain thread
            return TRUE;
          }

        } // end if control[c].handle

      } // end for loop
      if (matchedHandle)
      {
      return TRUE;
      } else
      {
        return 0;
      }

    } // end case WM_COMMAND

    case WM_NOTIFY:
    {
      return TRUE;
    }

    // Process other messages.
    default:
      return DefWindowProc(hMainWnd, uMsg, wParam, lParam);
  }
  return 0;

} // end MainWndProc callback


// 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
                           10,            // x position
                           10,            // 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 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

  } // 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;
}

// Find files in directory and display in the listbox
BOOL findFiles(HWND listBox, std::string startDir)
{
  WIN32_FIND_DATA FindFileData;

  int size;
  size = startDir.size();
  char *startVal = new char[size+1];
  startVal[size] = 0; // trailing null
  memcpy(startVal, startDir.c_str(), size);

  HANDLE hFind = FindFirstFile((LPCSTR)startVal, &FindFileData);

  int index = 0;

  if(hFind == INVALID_HANDLE_VALUE)
  {
    return false;
  }
  else do
  {
    // Add file name to array and send to listbox
    int pos = (int)SendMessage( listBox, LB_ADDSTRING, 0,
                                (LPARAM)FindFileData.cFileName );
    if (index < listBoxItemsMax)
    { listBoxItems[index].fileAttributes = FindFileData.dwFileAttributes;
      for (int f = 0; f < nameMaxSize; f++)
      {
        listBoxItems[index].fileName[f] = FindFileData.cFileName[f];
      }
      if (index > listBoxItemsCount)
      {
        listBoxItemsCount = index;
      }
    }
    // Set the array index of the file/folder as item data.
    // This will enable it to be retrieved from the array
    // even after the items are sorted by the list box.
    SendMessage(listBox, LB_SETITEMDATA, pos, (LPARAM) index);
    index++;

  } while (FindNextFile(hFind, &FindFileData));

  DWORD findError = GetLastError();
  if (findError != 18) // not end of file
  {
    std::string findErrorStr = "FindNextFile Error " + std::to_string(findError);
    std::cout << findErrorStr << '\n';
  }
  if (index > listBoxItemsCount) // for the last increment
  {
    listBoxItemsCount = index;
  }

  FindClose(hFind);
  return true;

} // end findFiles


// Create listbox and fill with Get the selected file via a List Box
// or empty if already exists.
BOOL fileLocate()
{
  if (hSelListBoxWnd == 0)
  {
    hSelListBoxWnd = CreateWindow
                     ( "LISTBOX",     // Predefined class; Unicode assumed
                       "Select File", // Title text
                       LBS_NOTIFY | WS_VISIBLE | WS_VSCROLL | WS_BORDER | WS_CHILD,
                       200,           // x position
                       10,            // y position
                       350,           // Box width
                       400,           // Box height
                       hMainWnd,      // Parent window
                       NULL,          // no menu
                       (HINSTANCE)GetWindowLong(hMainWnd, GWL_HINSTANCE),
                       NULL );        // Pointer not needed.
    control[numberOfControls].handle = hSelListBoxWnd; // Capture handle
    control[numberOfControls].name = "Select ListBox"; //  and name of the
    numberOfControls++;                                //  button control
  }
  else
  {
    emptyTheListBox();
  }

  // Select initial directory/folder
  startDir = ("C:/Source/*");

  // Fill the listbox with file names in the folder
  findFiles(hSelListBoxWnd,startDir);

  // Cause the controls to be displayed in the main window
  UpdateWindow(hMainWnd);

  locateFile = FALSE; // don't repeat call to this routine from forever loop

} // end fileLocate

// Empty the listbox and redisplay
BOOL emptyTheListBox()
{
  for (int i = 0; i < listBoxItemsCount; i++)
  {
    int j = listBoxItemsCount - 1 - i;

    SendMessage(hSelListBoxWnd, LB_DELETESTRING, j, 0);
  }

  UpdateWindow(hSelListBoxWnd); // cause the list box to be redisplayed

} // end emptyTheListBox


// Clear the ListBox and then add files of new selected directory
BOOL fileRelocate()
{

  // Save selected directory name
  CHAR directoryName[nameMaxSize];
  memcpy(directoryName,listBoxItems[dirListBoxIndex].fileName,nameMaxSize);

  emptyTheListBox();

  dirListBoxIndex = 0; // prepare for next selection

  listBoxItemsCount = 0; // empty listBoxItems array

  int last = startDir.length();
  std::string slash = "/";
  startDir.insert(last-1,slash);         // add extra / before *
  startDir.insert(last-1,directoryName); // insert new directory before last /

  findFiles(hSelListBoxWnd,startDir); // load list box from selected folder

  UpdateWindow(hSelListBoxWnd); // cause the list box to be redisplayed

  return FALSE; // don't repeat call to this routine until new folder selected

} // end fileRelocate


// File path to be returned via obtainFilePath
// Declared as static to avoid being on stack when returned
char filePath[64];

// Obtain file path by combining directory and file name
char* obtainFilePath()
{
  int last = startDir.length();
  memcpy(filePath,startDir.c_str(),last-1); // start with directory

  for (int i = 0; i < 64; i++) // append selected file to directory
  {
    filePath[last-1+i] = listBoxItems[fileListBoxIndex].fileName[i];
    if (listBoxItems[fileListBoxIndex].fileName[i] == 0)
    {
      break; // exit the loop after trailing null added to filePath
    }
  }

  return filePath;
} // end obtainFilePath


/*
 * 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 hResultsWnd1 = CreateWindow
                      ( "EDIT",           // predefined class
                        "Key and Value with", // label text
                        WS_CHILD | WS_VISIBLE | ES_LEFT,   // Styles
                        10, 125, 150, 20, // set size and location
                        hMainWnd,         // parent window
                        NULL,             // edit control ID
                        (HINSTANCE) GetWindowLong(hMainWnd, GWL_HINSTANCE),
                        NULL );           // pointer not needed
  HWND hResultsWnd2 = CreateWindow
                      ( "EDIT",           // predefined class
                        " greatest Sum", // label text
                        WS_CHILD | WS_VISIBLE | ES_LEFT,   // Styles
                        10, 150, 150, 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* filePath;
  filePath = obtainFilePath();

  FILE* file;
  char buffer[30];
  file = fopen(filePath, "r");

  int currPos;
  int lastPos = 0;
  int length;

  if(file == NULL)
  {
    perror("Error opening file");
    return FALSE;
  }
  else
  { // Read file records until end-of-file

    // Read each record/line of the file and Parse it.
    while (!feof(file))
    {
      fgets(buffer, 30, (FILE*)file);
      currPos = ftell(file);
      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(file);

    // Report the results.
    Report();

  }
  return TRUE;

} // end StartApp




No comments: