ACCU Home page ACCU Conference Page
Search Contact us ACCU at Flickr ACCU at GitHib ACCU at Facebook ACCU at Linked-in ACCU at Twitter Skip Navigation

pinWindows File Class

Overload Journal #3 - Aug 1993 + Programming Topics   Author: Ian Horwill

Requires Microsoft Windows 3.1.

This is an abstract class to support the saving and loading of the contents of a window to a file (i.e. the New, Open, Save and Save As options on the File menu). It uses the "Open" and "Save As" dialogs from the Windows Common Dialog Box Library.

CanDelete() controls whether or not the object can be deleted without losing data. If the object has changed since it was last saved, a message box is displayed asking if the file should be saved and giving the options "Yes", "No" and "Cancel".

If the user saves the file (CanDelete() calls SaveToFile() to do this) or chooses not to save it, CanDelete() returns TRUE. If the user cancels the message box or the "Save As" dialog box (using the Cancel button or the Close option on the system menu), CanDelete() returns FALSE.

LoadFromFile() can be called in response to the File|Open command. It asks the user to select a file using the standard "Open" dialog box. It then opens the file and calls ReadFile(), which must be defined in a derived class.

CanDelete() should be called before calling LoadFromFile(), to ensure that any changes are not lost.

SaveToFile() opens the file associated with the object and calls WriteFile() to write the object to the file. If no filename has yet been specified, or if the 'prompt' parameter is TRUE (such as when File|Save As is selected), this function requests one via the "Save As" common dialog box.

file_filter is the list of file type descriptions passed to the Common Dialog Box routines to specify which files should be shown in the list box. See the description of the lpstrFilter field in the Windows OPENFILENAME structure.

default_extn is passed to both the Open and Save dialog boxes for use if the user does not specify a filename extension.

HasChanged() is provided to allow a derived class to flag that the object has changed and should therefore be saved before being deleted. Note that the derived class is only allowed to set this flag (not clear it).

/*******************************************************
File:    FILEDOBJ.H
*******************************************************/

class FILED_OBJECT
{
public:
// Public read-only access to object's filename
const char* const filename;
BOOL CanDelete(char* app_name);
BOOL LoadFromFile();
BOOL SaveToFile(BOOL prompt);
protected:
FILED_OBJECT(HWND h) : hwnd(h),
filename(filename_buf),
file_filter(NULL),
default_extn(NULL),
has_filename(FALSE),
has_changed(FALSE)
         {}
HWND hwnd;
char filename_buf[256];  // Storage space for filename of object.
char* file_filter;
char* default_extn;
void HasChanged() { has_changed = TRUE; }
private:
BOOL has_filename;       // Is filename set up yet?
BOOL has_changed;
virtual void ReadFile(HFILE f) = 0;
virtual void WriteFile(HFILE f) = 0;
};

/*******************************************************
File:    FILEDOBJ.CPP
Description:
This file contains the definition of the FILED_OBJECT
class described in the header file FILEDOBJ.H.
*******************************************************/
#include <windows.h>
#include <commdlg.h>
#include "filedobj.h"

/*======================================================
Function:      CanDelete()
Parameters:
app_name - string to be used as title of message box.
Description:
This function is designed to be called before any operation
that will lose the object's data - e.g. selecting File|New,
File|Open or quitting from the application.

We make use of the 'has_changed' flag to determined
whether there are any outstanding changes. This flag is
cleared when the object is saved to or loaded from a file
and should be set by the derived class when the object
changes (by calling HasChanged()).

If the file HAS changed, we put up a message box allowing
the user to save the changes, abandon the changes or
cancel the operation. To save the changes we call
SaveToFile().

Return value:
If the object hasn't changed, the user doesn't want to
save the changes, or the changes are saved successfully,
we return TRUE - i.e. the object CAN be deleted.
Otherwise (i.e. the user selects cancel or the file can't
be saved), we return FALSE - the object CAN'T be deleted.
======================================================*/

BOOL FILED_OBJECT::CanDelete(char* app_name)
{
if (!has_changed) return TRUE; // No need to save the object

// Find the actual filename (first char after last backslash)
// Start at the end...and look backwards
for (char* name = filename_buf; *name; ++name)
while (name != fiIename_buf && name[-1] != '\\')
--name;

char msg[80];
wsprintf(msg,
"%s has changed.\nDo you want to save the changes?",
(LPSTR)name);
MessageBeep(MB_ICONQUESTION);
switch (MessageBox(hwnd, msg,
app_name,MB_ICONQUESTION|MB_YESNOCANCEL))
{
case IDYES :
return SaveToFile(FALSE); // Can delete if saved OK
case IDNO :
return TRUE; // Yes - you can delete me
case IDCANCEL :
default :
return FALSE; // No - don't you dare!
}
}

/*======================================================
Function:      LoadFromFile()
Description:
Using the common 'Open' dialog box, this function
prompts the user for the file that is to be opened,
opens the file and calls ReadFile() to read the object
from the file. The file is then closed.

Note that the user may only select an existing, writeable
file. We should therefore have no problems reading or
(later) writing the file.

Return value:
This function returns TRUE (i.e. 'success') if the file
is read OK, FALSE if the user cancelled from the 'Open'
dialog box.

Notes:
A temporary buffer is used to receive the new filename
so that we don't overwrite the current filename unless
a new one is successfully chosen.
======================================================*/
BOOL FILED OBJECT::LoadFromFile()
{
OPENFILENAME ofn;
char new_name[sizeof filename_buf];
new_name[0] = 0;
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.ipstrFilter = file_filter;
ofn.lpstrCustomFilter = NULL;
ofn.nFilterIndex = 1;
ofn.IpstrFile = new_name;
ofn.nMaxFile = sizeof new_name;
ofn.lpstrFileTitle = NULL;
ofn.lpstrInitialDir = NULL; // Use default directory
ofn.lpstrTitle = NULL;
ofn.Flags = OFN_FILEMUSTEXIST |
OFN__NOREADONLYRETURN |
OFN_HIDEREADONLY; // Extn: allow read-only
ofn.lpstrDefExt = default_extn;

if (!GetOpenFileName(&ofn)) return FALSE;

OFSTRUCT info; // Extn: share flags?, failure?

HFILE f = OpenFile(new_name, &info, OF_READ);

ReadFile(f); // Extn: handle failure (e.g. incorrect format)
_lclose(f);

lstrcpy(fiIename_buf,  new_name);
has_filename = TRUE;
has__changed = FALSE;
return TRUE;
}

/*======================================================
Function:      SaveToFile{}
Parameters:
prompt - If FALSE, only asks user for a filename if the
object doesn't already have one; if TRUE, asks for one
regardless.

Description:
This function opens the file associated with the object
(or selected by the user if 'prompt' is TRUE of the
object has no filename yet), calls WriteFile() to write
the object to the file and then closes the file.

If 'prompt' is TRUE and a filename already exists (e.g.
the user selected 'Save As' from the menu), the current
filename is displayed as the default.

Return value:
This function returns TRUE (i.e. 'success') if the file
is saved OK, FALSE if the user cancelled from the
'Save As' dialog box.
======================================================*/

BOOL FILED_OBJECT::SaveToFile(BOOL prompt)
{
OPENFILENAME ofn;

if (prompt || !has_filename) // Get filename from user
{
char new_name[sizeof filename_buf];
lstrcpy(new_name, filename);
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.ipstrFilter = file_filter;
ofn.lpstrCustomFilter = NULL;
ofn.nFilterIndex = 1;
ofn.lpstrFile = new_name;
ofn.nMaxFile = sizeof new_name;
ofn.lpstrFileTitle = NULL;
ofn.lpstrInitialDir = NULL; // Use default directory
ofn.lpstrTitle = NULL;
ofn.Flags = OFN_NOREADONLYRETURN |
OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT;
ofn.lpstrDefExt = default_extn;
if (!GetSaveFileName(&ofn)) return FALSE;
lstrcpy(filename_buf, new_name); has_filename = TRUE;
}

OFSTRUCT info; // Extn: share flags?, failure?
 HFILE f = OpenFile(filename_buf, &info,
OF_WRITE|OF_CREATE);

WriteFile(f);
_lclose(f);

has_changed = FALSE;
return TRUE;
}

Overload Journal #3 - Aug 1993 + Programming Topics