//	Zinc Application Framework - D_FILDLG.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <stdlib.h>
#include <z_mouse2.hpp>
#include <z_fildlg.hpp>
#include <z_string.hpp>
#include <z_border.hpp>
#include <z_title.hpp>
#include <z_prompt.hpp>
#include <z_vlist.hpp>
#include <z_combo.hpp>
#include <z_button.hpp>
#include <z_str1.hpp>
#include <z_scrll2.hpp>
#include <z_msgwin.hpp>
#include <z_error.hpp>
#include <z_utils.hpp>
#include <z_unistd.hpp>

#define ZAF_FILE_DIALOG_INFO
#include "lang_def.cpp"

extern "C"
{
#include <ctype.h>
#include <stdio.h>
#include <string.h>
}

#if !defined(USE_ZAF_CODE_SET_DATA)
static const char *dirSepStr = "\\";
#else
#define dirSepStr zafCodeSet->dirSepStr
#endif

// ----- Miscellaneous Compiler-Dependent Functions -----------------------

static ZafIChar ZAF_FARDATA _parentDirectoryName[] = { 'p','a','r','e','n','t','D','i','r','e','c','t','o','r','y', 0 };
static ZafIChar ZAF_FARDATA _currentDirectoryName[] = { 'c','u','r','r','e','n','t','D','i','r','e','c','t','o','r','y', 0 };
static ZafIChar ZAF_FARDATA _childDirectoryName[] = { 'c','h','i','l','d','D','i','r','e','c','t','o','r','y', 0 };
static ZafIChar ZAF_FARDATA _hardDriveName[] = { 'h','a','r','d','D','r','i','v','e', 0 };
static ZafIChar ZAF_FARDATA _networkDriveName[] = { 'n','e','t','w','o','r','k','D','r','i','v','e', 0 };
static ZafIChar ZAF_FARDATA _floppyDriveName[] = { 'f','l','o','p','p','y','D','r','i','v','e', 0 };
static ZafIChar ZAF_FARDATA _fileName[] = { 'f','i','l','e', 0 };
static ZafIChar ZAF_FARDATA _allDevices[]		= { '*','.','*', 0 };

const ZafEventType OPT_RESET_DRIVE		= 11000;
const ZafEventType OPT_RESET_DIRECTORY	= 11001;
const ZafEventType OPT_RESET_FILE		= 11002;
const ZafEventType OPT_RESET_FILE_LIST	= 11003;
const ZafEventType OPT_RESET_FILTER		= 11004;
const ZafEventType OPT_OK				= 11005;

static ZafNumberID DriveCharToId(char drive) { return (10100 + drive - 'a'); }
static ZafNumberID DriveIntToId(int drive) { return (10100 + drive); }
static int DriveCharToInt(char drive) { return (tolower(drive) - 'a'); }
static char DriveIntToChar(int drive) { return (drive + 'a'); }

#define DRIVE_ABORT 		2

#define IOCTL_CHANGEABLE	8
#define IOCTL_LOCAL_REMOTE	9

#if defined(__BCPLUSPLUS__) | defined(__TCPLUSPLUS__)
static int systemerror(int, int, int, int)
{
#	if !defined(ZAF_DOSEXTENDED)
	hardretn(DRIVE_ABORT);
#	endif
	return (DRIVE_ABORT);
}

#elif defined(_MSC_VER)
#	if defined(DOS386)
static void __cdecl systemerror(unsigned, unsigned, unsigned *)
#	else
static void __cdecl systemerror(unsigned, unsigned, unsigned far *)
#	endif
{
#	if !defined(DOS386)
	_hardresume(DRIVE_ABORT);
#	endif
}

#elif defined(__WATCOMC__)
static int __far systemerror(unsigned, unsigned, unsigned far *)
{
	return (DRIVE_ABORT);
}

#elif defined(__ZTC__) && !defined(__SC__)
static int __cdecl systemerror(int *, int *)
{
	return (DRIVE_ABORT);
}
#endif

static int Zafioctl(int handle, int functionID)
{
#if defined(_MSC_VER)
	union _REGS regs;
#else
	union REGS regs;
#endif

	// Check for a valid function number (8 or 9).
	if (functionID != 8 && functionID != 9)
		return (-1);
	regs.h.ah = 0x44;
	regs.h.al = functionID;
	regs.h.bl = handle;
#if defined(__ZTC__) && defined(DOS386)
	int86_real(0x21, &regs, &regs);
	return (regs.x.cflag ? -1 : (functionID == 8 ? regs.x.ax : regs.x.dx));
#elif defined(_MSC_VER)
	_int86(0x21, &regs, &regs);
	return (regs.x.cflag ? -1 : (functionID == 8 ? regs.x.ax : regs.x.dx));
#elif defined(__WATCOMC__)
	int386(0x21, &regs, &regs);
	return (regs.x.cflag ? -1 : (functionID == 8 ? regs.w.ax : regs.w.dx));
#elif defined(DOS32PP)
	int386(0x21, &regs, &regs);
	return (regs.x.cflag ? -1 : (functionID == 8 ? regs.x.eax : regs.x.edx));
#else
	int86(0x21, &regs, &regs);
	return (regs.x.cflag ? -1 : (functionID == 8 ? regs.x.ax : regs.x.dx));
#endif
}

static DeviceClassification drivetype(int drive)
{
	// Set the critical error handler.
	static int seterror = true;
	if (seterror)
	{
#if defined(__BCPLUSPLUS__) | defined(__TCPLUSPLUS__)
#	if !defined(DOS16PP) && !defined(DOS32PP)
		harderr(systemerror);
#	endif
#elif defined(_MSC_VER) || defined(__WATCOMC__)
#	if !defined(DOS386)
		_harderr(systemerror);
#	endif
#elif defined(__ZTC__) && !defined(__SC__)
		_cerror_handler = systemerror;
		cerror_open();
#endif
		seterror = false;
	}

	// Determine the drive type.
	DeviceClassification driveType = DEVICE_UNKNOWN;
	int error = Zafioctl(drive + 1, IOCTL_LOCAL_REMOTE);
	if (error == -1)
		;
	else if (error & 0x1000)
		driveType = DEVICE_DRIVE_REMOTE;
	else if (Zafioctl(drive + 1, IOCTL_CHANGEABLE) == 1)
		driveType = DEVICE_DRIVE_FIXED;
	else
		driveType = DEVICE_DRIVE_REMOVABLE;
	return (driveType);
}

ZafEventType FilterCallback(ZafWindowObject *, ZafEventStruct &event, ZafEventType ccode)
{
	if (ccode == L_SELECT)
	{
		event.type = OPT_RESET_FILTER;
		zafEventManager->Put(event);
	}
	return (0);
}

ZafEventType FileCallback(ZafWindowObject *object, ZafEventStruct &event, ZafEventType ccode)
{
	if (ccode == L_SELECT || ccode == N_NON_CURRENT)
	{
		if (!object->Changed())
			return (0);
		ZafFileDialog *filDlg = DynamicPtrCast(object->parent, ZafFileDialog);
		filDlg->SetFilter(object->Text());
		event.type = OPT_RESET_FILE_LIST;
		event.route = filDlg;
		object->eventManager->Put(event);
		object->SetChanged(false);
//		filDlg->Event(event);
	}
	return (0);
}


// ----- CriticalErrorHandler ---------------------------------------------

#if defined(__BCPLUSPLUS__) | defined(__TCPLUSPLUS__)
static int CriticalErrorHandler(int, int, int, int)
{
	errno = ENODEV;
#	if !defined(DOS16PP) && !defined(DOS32PP)
	_hardresume(_HARDERR_FAIL);
#	endif
	return 2; // ABORT
}

#elif defined(__ZTC__) && !defined(__SC__)
int _cdecl DOS::CriticalErrorHandler(int *ax, int *di)
{
	errno = ENODEV;
	*ax = _HARDERR_FAIL;
	return 2; // ABORT
}

#elif defined(_MSC_VER)
#	if defined(DOS386)
void CriticalErrorHandler(unsigned, unsigned, unsigned *)
#	else
void CriticalErrorHandler(unsigned, unsigned, unsigned far *)
#	endif
{
#	if !defined(DOS386)
	_hardresume(_HARDERR_FAIL);
#	endif
}

#elif defined(__WATCOMC__)
int CriticalErrorHandler(unsigned deverr, unsigned errcode, unsigned far *)
{
	_hardresume(_HARDERR_FAIL);
	return 2; // ABORT
}
#endif

// ----- ZafFileDialog ----------------------------------------------------

ZafFileDialog::ZafFileDialog(int left, int top, int, int) :
	ZafDialogWindow(left, top, 66, 10)
{
	// Get the error stings.
	messages = ZafLanguageManager::Allocate(className, defaultMessages);

	directory[0] = file[0] = filter[0] = 0;
	SetModal(true);
	SetSizeable(false);

	// Add the fields to the file dialog.
	Add(new ZafBorder);
	Add(new ZafTitle(ZAF_ITEXT("")));
	ZafPrompt *fileNamePrompt = new ZafPrompt(1, 0, 20, ZAF_ITEXT("File name:"));
	fileNamePrompt->SetHotKey('n', 5);
	Add(fileNamePrompt);
	ZafString *fileString = new ZafString(1, 1, 20, ZAF_NULLP(ZafStringData));
	fileString->SetStringID(ZAF_ITEXT("FILE_FIELD"));
	fileString->SetUserFunction(FileCallback);
	Add(fileString);
	ZafVtList *fileList = new ZafVtList(1, 2, 20, 5);
	fileList->SetStringID(ZAF_ITEXT("FILE_LIST"));
	fileList->SetAutoSortData(true);
	ZafScrollBar *scroll = new ZafScrollBar(0, 0, 0, 0, ZAF_NULLP(ZafScrollData));
	scroll->SetScrollType(ZAF_VERTICAL_SCROLL);
	fileList->Add(scroll);
	Add(fileList);
	ZafPrompt *filterPrompt = new ZafPrompt(1, 7, 20, ZAF_ITEXT("List files of type:"));
	filterPrompt->SetHotKey('t', 14);
	Add(filterPrompt);
	ZafComboBox *filterCombo = new ZafComboBox(1, 8, 20, 5);
	filterCombo->SetStringID(ZAF_ITEXT("FILTER_LIST"));
	ZafWindowObject *filterObject = new ZafString(0, 0, 0, new ZafStringData(_allDevices, 16));
	filterObject->SetUserFunction(FilterCallback);
	filterCombo->Add(filterObject, filterCombo->First());
	scroll = new ZafScrollBar(0, 0, 0, 0, ZAF_NULLP(ZafScrollData));
	scroll->SetScrollType(ZAF_VERTICAL_SCROLL);
	filterCombo->Add(scroll);
	Add(filterCombo);
	ZafPrompt *folderPrompt = new ZafPrompt(23, 0, 25, ZAF_ITEXT("Folders:"));
	folderPrompt->SetHotKey('f', 0);
	Add(folderPrompt);
	ZafVtList *folderList = new ZafVtList(23, 2, 25, 5);
	folderList->SetStringID(ZAF_ITEXT("FOLDER_LIST"));
	folderList->SetOSDraw(false);
	scroll = new ZafScrollBar(0, 0, 0, 0, ZAF_NULLP(ZafScrollData));
	scroll->SetScrollType(ZAF_VERTICAL_SCROLL);
	folderList->Add(scroll);
	Add(folderList);
	ZafPrompt *folderField = new ZafPrompt(23, 1, 25, ZAF_NULLP(ZafIChar));
	folderField->SetStringID(ZAF_ITEXT("FOLDER_FIELD"));
	folderField->SetAutoSize(false);
	Add(folderField);
	ZafPrompt *drivePrompt = new ZafPrompt(23, 7, 25, ZAF_ITEXT("Drives:"));
	drivePrompt->SetHotKey('v', 3);
	Add(drivePrompt);
	ZafComboBox *driveCombo = new ZafComboBox(23, 8, 25, 5);
	driveCombo->SetStringID(ZAF_ITEXT("DRIVE_COMBO"));
	driveCombo->SetOSDraw(false);
	scroll = new ZafScrollBar(0, 0, 0, 0, ZAF_NULLP(ZafScrollData));
	scroll->SetScrollType(ZAF_VERTICAL_SCROLL);
	driveCombo->Add(scroll);
	Add(driveCombo);
	ZafButton *okButton = new ZafButton(50, 1, 15, 1, ZAF_ITEXT("OK"));
	okButton->SetAllowDefault(true);
	okButton->SetHotKey((ZafIChar)'O', 0);
	okButton->SetSendMessageWhenSelected(true);
	okButton->SetValue(OPT_OK);
	okButton->SetStringID(ZAF_ITEXT("OK_BUTTON"));
	Add(okButton);
	ZafButton *cancelButton = new ZafButton(50, 3, 15, 1, ZAF_ITEXT("Cancel"));
	cancelButton->SetAllowDefault(true);
	cancelButton->SetHotKey((ZafIChar)'C', 0);
	cancelButton->SetSendMessageWhenSelected(true);
	cancelButton->SetValue(S_DLG_CANCEL);
	Add(cancelButton);
	SetDefaultButton(okButton);

	requestedFile[0] = '\0';

	originalDrive = currentDrive = GetDrive();
	GetPath(originalPath);

	strcpy(currentPath, originalPath);

	// Set up the drive, directory and file fields.
	DriveUpdate();
	DirectoryUpdate();

	// hook up the critical error handler.
#if defined(__BCPLUSPLUS__) | defined(__TCPLUSPLUS__)
#	if !defined(DOS16PP) && !defined(DOS32PP)
   	harderr(CriticalErrorHandler);
#	endif

#elif defined(__ZTC__) && !defined(__SC__)
	_cerror_handler = CriticalErrorHandler;
	cerror_open();

#elif defined(_MSC_VER)
#	if !defined(DOS386)
	_harderr(CriticalErrorHandler);
#	endif

#elif defined(__WATCOMC__)
	_harderr(CriticalErrorHandler);
#endif
}

void ZafFileDialog::DirectoryUpdate(void)
{
	DeviceInfo device;

	// Set up the Directory field.
	ZafVtList *folderList = DynamicPtrCast(GetObject("FOLDER_LIST"), ZafVtList);
	if (folderList)
	{
		folderList->SetAutomaticUpdate(false);
		folderList->Destroy();

		int indentation = 0;
		ZafDirectoryItem *cDirectoryField = ZAF_NULLP(ZafDirectoryItem);
		if (*currentPath)
		{
			ZafIChar subString[ZAF_MAXPATHLEN];
			strcpy(subString, currentPath);
			int length = strlen(subString);
			strrepc(subString, dirSepStr[0], '\0');
			for (int i = 0; i < length; )
			{
				int j = i + strlen(&subString[i]) + 1;
				cDirectoryField = new ZafDirectoryItem(indentation++, &subString[i],
					(j < length) ? _parentDirectoryName : _currentDirectoryName);
				cDirectoryField->userFlags = DEVICE_DIRECTORY;
				folderList->Add(cDirectoryField);
				if (j >= length)
					cDirectoryField->SetSelected(true);
				i = j;
			}
		}

		ZafList list;
		for (int error = FindFirst(_allDevices, device, DEVICE_DIRECTORY); !error; error = FindNext(device))
			if (device.Inherited(DEVICE_DIRECTORY) &&
				strcmp(device.name, ZafFileSystem::currentDirectoryName) &&
				strcmp(device.name, ZafFileSystem::parentDirectoryName))
			{
				list.SetCompareFunction((ZafCompareFunction)CompareAscending);
				list.Add(new ZafDirectoryItem(indentation, device.name, _childDirectoryName));
			}
		while (list.First())
		{
			ZafWindowObject *listItem = (ZafWindowObject *)list.First();
			list.Subtract(listItem);
			folderList->Add(listItem);
		}

		folderList->SetAutomaticUpdate(true);
	}
}

void ZafFileDialog::DriveUpdate(void)
{
	DeviceInfo device;
	ZafEventStruct event(S_ADD_OBJECT);

	// Set up the Drive field.
	ZafComboBox *driveList = DynamicPtrCast(GetObject("DRIVE_COMBO"), ZafComboBox);
	if (driveList)
	{
		driveList->SetAutomaticUpdate(false);
		driveList->Destroy();

		ZafWindowObject *cDriveField = ZAF_NULLP(ZafWindowObject);
		for (int error = FindFirst(_allDevices, device, DEVICE_DRIVE); !error; error = FindNext(device))
		{
			ZafIChar *itemType = _hardDriveName;
			if (device.Inherited(DEVICE_DRIVE_REMOTE))
				itemType = _networkDriveName;
			else if (device.Inherited(DEVICE_DRIVE_REMOVABLE))
				itemType = _floppyDriveName;
			ZafDirectoryItem *button = new ZafDirectoryItem(0, device.name, itemType);
			button->SetNumberID(DriveCharToId(*device.name));
			event.data = button;
			if (DriveCharToInt(*device.name) == currentDrive)
				cDriveField = button;
			driveList->Event(event);
		}

		if (cDriveField)
			cDriveField->SetSelected(true);

		driveList->SetAutomaticUpdate(true);
	}
}

ZafEventType ZafFileDialog::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = LogicalEvent(event);

	switch (ccode)
	{
	case OPT_RESET_DRIVE:
	case OPT_RESET_DIRECTORY:
		// Reset the drive or directory information.
		{
			ZafIChar *data = event.text;
			if (ccode == OPT_RESET_DRIVE)
			{
				// Set the drive response information.
				ZafEventType errorCode = false;
				ZafMessageWindow *errorWindow = ZAF_NULLP(ZafMessageWindow);
				eventManager->SetDeviceState(E_MOUSE, DM_WAIT);

				// Test the new drive.
				while (SetDrive(DriveCharToInt(*data)))
				{
//					errorCode = true;
					// Prepare the error message.
					if (!errorWindow)
					{
/* START BLOCK COMMENT
**							ZafIChar *title = response ?
**								msgTable.GetMessage(response - ZMSG_TITLES) :
**								msgTable.GetMessage(ZMSG_FILE_TNONE);
**							ZafIChar *message = msgTable.GetMessage(ZMSG_DRIVE_ERROR);
**							errorWindow = new ZafMessageWindow(title, ZAF_NULLP(ZafIChar),
**								ZAF_MSG_CANCEL | ZAF_MSG_RETRY,
**								ZAF_MSG_CANCEL, message, *data);
END BLOCK COMMENT */
						errorWindow = new ZafMessageWindow(new ZafStringData("Drive Error"),
							ZAF_NULLH(ZafIconImage), ZAF_DIALOG_CANCEL | ZAF_DIALOG_RETRY,
							ZAF_DIALOG_CANCEL, ZAF_ITEXT("The drive is screwed up."));
					}

					// Display the drive error.
					ZafErrorStub::Beep();
					errorCode = errorWindow->Control();
					if (errorCode == S_DLG_CANCEL)
					{
						errorCode = true;
						break;
					}
					else
					{
						eventManager->SetDeviceState(E_MOUSE, DM_WAIT);
						errorCode = false;
					}
				}

				// Delete the error window, if any.
				if (errorWindow)
					delete errorWindow;
				eventManager->SetDeviceState(E_MOUSE, DM_VIEW);

				// Re-add the old drive information.
				if (errorCode)
				{
					ZafWindowObject *oldDrive = GetObject(DriveIntToId(currentDrive));
					if (oldDrive)
						oldDrive->SetFocus(true);
					else
						ZafErrorStub::Beep();
					break;
				}
			}
			else
			{
				// Set the directory.
				if (SetPath(data))
					break; // error on setpath.
			}

			// Reset the drive and directory, then update the lists.
			eventManager->SetDeviceState(E_MOUSE, DM_WAIT);
			currentDrive = GetDrive();
			GetPath(currentPath);
			GetObject("FOLDER_FIELD")->SetText(currentPath);

			DirectoryUpdate();
			FileUpdate();
			eventManager->SetDeviceState(E_MOUSE, DM_VIEW);
		}
		break;

	case OPT_RESET_FILE_LIST:
		FileUpdate();
		break;

	case OPT_RESET_FILE:
		// Reset the file selection.
		eventManager->SetDeviceState(E_MOUSE, DM_WAIT);
		GetObject("FILE_FIELD")->SetText(event.text);
		eventManager->SetDeviceState(E_MOUSE, DM_VIEW);
		break;

	case OPT_RESET_FILTER:
		{
			// Reset the filter selection.
			eventManager->SetDeviceState(E_MOUSE, DM_WAIT);
			ZafWindowObject *filterList = GetObject("FILTER_LIST");
			if (filterList->Text())
				strcpy(filter, filterList->Text());
			else
				filter[0] = '\0';
			GetObject("FILE_FIELD")->SetText(filter);
			FileUpdate();
			eventManager->SetDeviceState(E_MOUSE, DM_VIEW);
		}
		break;

	case OPT_OK:
		{
			if (!strcmp(ZAF_ITEXT("FILE_LIST"), current->StringID()))
			{
				DynamicPtrCast(Current(), ZafWindow)->Current()->Event(L_SELECT);
				eventManager->Put(S_DLG_OK);
			}
			else if (!strcmp(ZAF_ITEXT("FOLDER_LIST"), current->StringID()))
				DynamicPtrCast(Current(), ZafWindow)->Current()->Event(L_SELECT);
			else if (!strcmp(ZAF_ITEXT("OK_BUTTON"), current->StringID()))
			{
				if (windowManager->oldFocusObject->parent &&
					!strcmp(windowManager->oldFocusObject->parent->StringID(), ZAF_ITEXT("FOLDER_LIST")))
					windowManager->oldFocusObject->Event(L_SELECT);
				else
				{
					ZafWindowObject *field = GetObject("FILE_FIELD");
					const ZafIChar *text = field->Text();
					if (text && (strchr(text, '*') || strchr(text, '?')))
						field->Event(L_SELECT);
					else
						eventManager->Put(S_DLG_OK);
				}
			}
			else
			{
				ZafWindowObject *field = GetObject("FILE_FIELD");
				const ZafIChar *text = field->Text();
				if (text && (strchr(text, '*') || strchr(text, '?')))
					field->Event(L_SELECT);
				else
					eventManager->Put(S_DLG_OK);
			}
/* START BLOCK COMMENT
**				else
**					eventManager->Put(S_DLG_OK);
END BLOCK COMMENT */
		}
		break;

	default:
		ccode = ZafDialogWindow::Event(event);
		break;
	}

	return (ccode);
}

void ZafFileDialog::FileUpdate(void)
{
	DeviceInfo device;

	// Set up the File field.
	ZafVtList *fileList = DynamicPtrCast(GetObject("FILE_LIST"), ZafVtList);
	if (fileList)
	{
		fileList->SetAutomaticUpdate(false);
		fileList->Destroy();

		for (int error = FindFirst(_allDevices, device, DEVICE_FILE); !error; error = FindNext(device))
			if (device.Inherited(DEVICE_FILE) &&
				!WildStrcmp(device.name, filter))
			{
				fileList->Add(new ZafDirectoryItem(0, device.name, _fileName));
			}

		fileList->SetCurrent(fileList->First());
		fileList->Event(S_VSCROLL_CHECK);
		fileList->SetAutomaticUpdate(true);
	}
}

int ZafFileDialog::FindFirst(const ZafIChar *fullName, DeviceInfo &info, DeviceClassification classification)
{
	info.reserved = 0;
	info.classification = classification;
#if defined(__ZTC__)
	info._reserved = ZAF_NULLP(void);
#endif

	// ----- drive code. -----
	if (info.Inherited(DEVICE_DRIVE))
		return (FindNext(info));

	// ----- directory and file code. -----
#if defined(__BCPLUSPLUS__) | defined(__TCPLUSPLUS__)
#	if defined(USE_ZAF_CODE_SET_DATA)
	char *_fullName = zafCodeSet->ConvertToOSString(fullName);
#	else
	char *_fullName = strdup(fullName);
#	endif
	int error = ::findfirst(_fullName, &info._reserved, FA_DIREC);
	delete _fullName;
#elif defined(_MSC_VER) || defined(__WATCOMC__)
#	if defined(USE_ZAF_CODE_SET_DATA)
	char *_fullName = zafCodeSet->ConvertToOSString(fullName);
#	else
	char *_fullName = strdup(fullName);
#	endif
	int error = _dos_findfirst(_fullName, _A_SUBDIR, &info._reserved) ? -1 : 0;
	delete _fullName;
#elif defined(__ZTC__)
	info._reserved = ::findfirst(fullName, FA_DIREC);
	int error = info._reserved ? 0 : -1;
#endif

	if (!error)
		return (FindNext(info));
	return (error);
}

int ZafFileDialog::FindNext(DeviceInfo &info)
{
	const int LAST_DRIVE = 25;

	// ----- drive code. -----
	if (info.Inherited(DEVICE_DRIVE))
	{
#if defined(__BCPLUSPLUS__) | defined(__TCPLUSPLUS__)
		int drive = info.reserved ? (int)info._reserved.ff_fsize : 0;
#elif defined(_MSC_VER) || defined(__WATCOMC__)
		int drive = info.reserved ? (int)info._reserved.size : 0;
#elif defined(__ZTC__)
		static struct FIND __reserved;
		int drive = info._reserved ? (int)info._reserved->size : 0;
		info._reserved = &__reserved;
#endif

		DeviceClassification driveType = drivetype(drive);
		while (!driveType && drive <= LAST_DRIVE)
		{
			drive++;
			driveType = drivetype(drive);
		}
		if (driveType)
		{
			static ZafIChar _1Char[] = { '%','c',':', 0 };
			info.reserved++;
			info.classification = driveType;
			sprintf(info.name, _1Char, DriveIntToChar(drive));
			info.date.SetDate(ZafDateData());
			info.time.SetTime(ZafTimeData());
			info.size.SetBignum(0L);
#if defined(__BCPLUSPLUS__) | defined(__TCPLUSPLUS__)
			info._reserved.ff_fsize = drive + 1;
#elif defined(_MSC_VER) || defined(__WATCOMC__)
			info._reserved.size = drive + 1;
#elif defined(__ZTC__)
			info._reserved->size = drive + 1;
#endif
		}

		return (driveType ? 0 : -1);
	}

	// ----- directory and file code. -----
#if defined(__BCPLUSPLUS__) | defined(__TCPLUSPLUS__)
	int error = info.reserved ? ::findnext(&info._reserved) : 0;
	if (!error)
	{
		info.reserved++;
		info.classification = (info._reserved.ff_attrib == FA_DIREC) ? DEVICE_DIRECTORY : DEVICE_FILE;
		ZafIChar _name[ZAF_MAXPATHLEN];
#	if defined(USE_ZAF_CODE_SET_DATA)
		zafCodeSet->ConvertToZafString(info._reserved.ff_name, _name, false);
#	else
		strcpy(_name, info._reserved.ff_name);
#	endif
		strcpy(info.name, _name);
		strlwr(info.name);
	}
#elif defined(_MSC_VER) || defined(__WATCOMC__)
	int error = info.reserved ? (::_dos_findnext(&info._reserved) ? -1 : 0) : 0;
	if (!error)
	{
		info.reserved++;
		info.classification = (info._reserved.attrib == _A_SUBDIR) ? DEVICE_DIRECTORY : DEVICE_FILE;
		ZafIChar _name[ZAF_MAXPATHLEN];
#if defined(USE_ZAF_CODE_SET_DATA)
		zafCodeSet->ConvertToZafString(info._reserved.name, _name, false);
#else
		strcpy(_name, info._reserved.name);
#endif
		strcpy(info.name, _name);
		strlwr(info.name);
	}
#elif defined(__ZTC__)
	if (info.reserved)
		info._reserved = ::findnext();
	int error = info._reserved ? 0 : -1;
	if (!error)
	{
		info.reserved++;
		info.classification = (info._reserved->attribute == FA_DIREC) ? DEVICE_DIRECTORY : DEVICE_FILE;
		strcpy(info.name, info._reserved->name);
		strlwr(info.name);
	}
#endif

	return (error);
}

void ZafFileDialog::FullPath(ZafIChar *pathBuffer, int bufferSize)
{
	// Copy directory string.
	strncpy(pathBuffer, directory, bufferSize);
	int bufferIndex = strlen(directory);

	// Append a directory separator character if neccessary.
	if (pathBuffer[bufferIndex - 1] != ZafOSDirSepStr()[0])
	{
		strncpy(&pathBuffer[bufferIndex], ZafOSDirSepStr(), bufferSize - bufferIndex);
		bufferIndex++;
	}

	// Append the file name.
	strncpy(&pathBuffer[bufferIndex], file, bufferSize - bufferIndex);
}

int ZafFileDialog::GetDrive(void)
{
	unsigned drive;
	_dos_getdrive(&drive);
	return (drive - 1);
}

ZafDialogEvent ZafFileDialog::GetFile(ZafFileDialogRequest request)
{
	switch (request)
	{
	case ZAF_FILE_DIALOG_NEW:
		SetText(messages->GetMessage(ZAF_MSG_NEW));
		break;

	case ZAF_FILE_DIALOG_OPEN:
		SetText(messages->GetMessage(ZAF_MSG_OPEN));
		break;

	case ZAF_FILE_DIALOG_SAVEAS:
		SetText(messages->GetMessage(ZAF_MSG_SAVEAS));
		break;
	}

	if (!*filter)
	{
		ZafWindowObject *filterList = GetObject("FILTER_LIST");
		if (filterList->Text())
			strcpy(filter, filterList->Text());
	}

	// Set up the directory and filter strings.
	ZafWindowObject *folderField = GetObject("FOLDER_FIELD");
	folderField->SetText(currentPath);
	ZafWindowObject *fileField = GetObject("FILE_FIELD");
	fileField->SetText(filter);

	ZafComboBox *filterList = DynamicPtrCast(GetObject("FILTER_LIST"), ZafComboBox);
	if (filterList && *filter)
	{
		ZafWindowObject *object = ZAF_NULLP(ZafWindowObject);
		for (object = filterList->First(); object; object = object->Next())
		{
			if (!strcmp(object->Text(), filter))
				break;
		}

		// Found a filter that matches 'filter'?
		if (!object)
		{
			ZafWindowObject *filterObject = new ZafString(0, 0, 0, new ZafStringData(filter, 16));
			filterObject->SetUserFunction(FilterCallback);
			filterList->Add(filterObject, filterList->First());
			filterObject->SetSelected(true);
		}
	}
	FileUpdate();

	// Get the file and directory information.
	ZafEventType result = Control();
	if (result == S_DLG_OK)
	{
		strcpy(file, fileField->Text());
		strcpy(directory, folderField->Text());
	}
	else
		file[0] = directory[0] = 0;

	return (result);
}

int ZafFileDialog::GetPath(ZafIChar *pathName)
{
	int error = getcwd(pathName, ZAF_MAXPATHLEN) ? 0 : -1;
	if (!error)
		strlwr(pathName);
	return (error);
}

int ZafFileDialog::SetDrive(int drive)
{
	ZafIChar tempPath[ZAF_MAXPATHLEN];
	unsigned temp, oldDrive;
	_dos_getdrive(&oldDrive);
	_dos_setdrive(drive + 1, &temp);
	_dos_getdrive(&temp);

	if ((temp - 1 == drive) && getcwd(tempPath, ZAF_MAXPATHLEN))
		return (0);
	_dos_setdrive(oldDrive, &temp);
	return (-1);
}

int ZafFileDialog::SetPath(ZafIChar *pathName)
{
	return (chdir(pathName));
}

OSWindowID ZafFileDialog::OSScreenID(ZafScreenIDType) const
{
	return (screenID);
}

void ZafFileDialog::OSRegisterObject(void)
{
	ZafDialogWindow::OSRegisterObject();
}

void ZafFileDialog::OSSize(void)
{
	ZafDialogWindow::OSSize();
}

// ----- ZafDirectoryItem -------------------------------------------------

#define ZAF_FILE_DIALOG_INFO
#include "img_def.cpp"
ZafDirectoryItem::ZafItemSet ZAF_FARDATA ZafDirectoryItem::_item[] =
{
	{ _parentDirectoryName, 	0, ParentDirectoryCallback },
	{ _childDirectoryName, 		0, DirectoryCallback },
	{ _currentDirectoryName,	0, 0 },
	{ _hardDriveName, 			0, DriveCallback},
	{ _networkDriveName, 		0, DriveCallback },
	{ _floppyDriveName, 			0, DriveCallback },
	{ _fileName,	 				0, FileCallback },
	{ 0, 								0, 0 }
};

ZafDirectoryItem::ZafDirectoryItem(int zIndentation, ZafIChar *pathName, ZafIChar *type) :
	// Initialize the base class
	ZafButton(0, 0, 22, 1, pathName),
	indentation(zIndentation)
{
	SetButtonType(ZAF_FLAT_BUTTON);
	SetAutoSize(false);
	SetSelectOnDoubleClick(true);
	SetHzJustify(ZAF_HZ_LEFT);

	ImageAllocate();

	// Find the matching item.
	for (int i = 0; _item[i].name; i++)
		if (!stricmp(_item[i].name, type) || 	// the item matches.
			!_item[i+1].name)					// default to persist.
		{
			SetBitmapData(_item[i].bitmapData);
			SetUserFunction(_item[i].userFunction);
			break;
		}
}

ZafDirectoryItem::~ZafDirectoryItem(void)
{
	ImageFree();
}

ZafEventType ZafDirectoryItem::DirectoryCallback(ZafWindowObject *object, ZafEventStruct &event, ZafEventType ccode)
{
	if (ccode == L_DOUBLE_CLICK || object->LogicalEvent(event) == L_SELECT)
	{
		event.type = OPT_RESET_DIRECTORY;
		event.text = (ZafIChar *)object->Text();
		object->eventManager->Put(event);
	}
	return (0);
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafDirectoryItem::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	// Compute the actual draw region.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();

	display->SetClipRegion(event.region);

	// Don't draw if everything will be clipped.
	if (!drawRegion.Overlap(display->ClipRegion()))
	{
		EndDraw();
		return (ccode);
	}

	DrawBackground(drawRegion, ccode);

	ZafPaletteStruct textPalette = LogicalPalette(ZAF_PM_TEXT, state);
	display->SetPalette(textPalette);
	drawRegion.left += indentation * display->cellWidth;
	if (bitmapData)
	{
		int top = drawRegion.Height() / 2 - bitmapData->Height() / 2;
		display->Bitmap(drawRegion.left, top, *bitmapData);
		drawRegion.left += bitmapData->Width() + display->cellWidth;
	}
	display->Text(drawRegion, Text());
	if (Focus())
		DrawFocus(drawRegion, ccode);

	EndDraw();
	return (ccode);
}
#endif

ZafEventType ZafDirectoryItem::DriveCallback(ZafWindowObject *object, ZafEventStruct &event, ZafEventType ccode)
{
	if (ccode == L_SELECT)
	{
		event.type = OPT_RESET_DRIVE;
		event.text = (ZafIChar *)object->Text();
		object->eventManager->Put(event);
	}
	return (0);
}

ZafEventType ZafDirectoryItem::FileCallback(ZafWindowObject *object, ZafEventStruct &event, ZafEventType ccode)
{
	if (ccode == L_DOUBLE_CLICK || ccode == L_SELECT)
	{
		event.type = OPT_RESET_FILE;
		event.text = (ZafIChar *)object->Text();
		object->eventManager->Put(event);

		if (ccode == L_DOUBLE_CLICK || object->LogicalEvent(event) == L_SELECT)
		{
			event.type = S_DLG_OK;
			object->eventManager->Put(event);
		}
	}

	return (0);
}

void ZafDirectoryItem::ImageAllocate(void)
{
#if !defined(ZAF_TEXT_MODE)
	// Initialize the images.
	if (!_item[0].bitmapData)
	{
		ZafI18nData::AddStaticModule(ImageFree);
		for (int i = 0; _item[i].name; i++)
		{
			if (!strcmp(_item[i].name, _parentDirectoryName))
				_item[i].bitmapData = new ZafBitmapData(_openFile_ZafBitmap);
			else if (!strcmp(_item[i].name, _childDirectoryName))
				_item[i].bitmapData = new ZafBitmapData(_closedFile_ZafBitmap);
			else if (!strcmp(_item[i].name, _currentDirectoryName))
				_item[i].bitmapData = new ZafBitmapData(_openFile_ZafBitmap);
			else if (!strcmp(_item[i].name, _floppyDriveName))
				_item[i].bitmapData = new ZafBitmapData(_floppyDrive_ZafBitmap);
			else if (!strcmp(_item[i].name, _hardDriveName))
				_item[i].bitmapData = new ZafBitmapData(_hardDrive_ZafBitmap);
			else if (!strcmp(_item[i].name, _networkDriveName))
				_item[i].bitmapData = new ZafBitmapData(_networkDrive_ZafBitmap);

			if (_item[i].bitmapData)
			{
				_item[i].bitmapData->staticHandle = true;
				_item[i].bitmapData->SetDestroyable(false);
			}
		}
	}
#endif
}

void ZafDirectoryItem::ImageFree(bool globalRequest)
{
#if !defined(ZAF_TEXT_MODE)
	// Destroy the static images.
	if (globalRequest && _item[0].bitmapData)
	{
		for (int i = 0; _item[i].name; i++)
		{
			if (_item[i].bitmapData)
			{
				_item[i].bitmapData->staticHandle = false;
				delete _item[i].bitmapData;
				_item[i].bitmapData = ZAF_NULLP(ZafBitmapData);
			}
		}
	}
#endif
}

ZafEventType ZafDirectoryItem::ParentDirectoryCallback(ZafWindowObject *object, ZafEventStruct &event, ZafEventType ccode)
{
	static ZafIChar _fullPath[ZAF_MAXPATHLEN];
	if (ccode == L_DOUBLE_CLICK || object->LogicalEvent(event) == L_SELECT)
	{
		event.type = OPT_RESET_DIRECTORY;
		ZafWindowObject *tObject = object;
		while (tObject->Previous())
			tObject = tObject->Previous();
		_fullPath[0] = '\0';
		while (tObject != object)
		{
			strcat(_fullPath, tObject->Text());
			strcat(_fullPath, dirSepStr);
			tObject = tObject->Next();
		}
		strcat(_fullPath, object->Text());
		if (!object->Previous())
			strcat(_fullPath, dirSepStr);
		event.text = _fullPath;
		object->eventManager->Put(event);
	}
	return (0);
}

