/****************************************************************************
Desc: This routine shuts down all threads in the NLM.
****************************************************************************/
void domEditCleanup( void)
{
	gv_bShutdown = TRUE;
	while( gv_bRunning)
	{
		f_sleep( 10);
	}
}
/****************************************************************************
Desc:		After commit and before we unlock, stop and start all indexing.
****************************************************************************/
void flmIndexingAfterCommit(
	FDB *					pDb)
{
	F_BKGND_IX *		pStartIx;
	F_BKGND_IX *		pStopIx;
	F_BKGND_IX *		pNextIx;
	FLMBOOL				bThreadsActive;
	FLMBOOL				bStopped;

	// Signal all background indexing threads in the stop list
	// to shutdown.  Poll until all have terminated.

	for( ;;)
	{
		bThreadsActive = FALSE;
		for( pStopIx = pDb->pIxStopList; pStopIx; pStopIx = pStopIx->pNext)
		{
			stopBackgroundIndexThread( pDb, pStopIx->indexStatus.uiIndexNum, 
				FALSE, &bStopped);
			if( !bStopped)
			{
				bThreadsActive = TRUE;
			}
		}

		if( !bThreadsActive)
		{
			break;
		}

		f_sleep( 50);
	}

	// Now that all of the threads have been stopped, discard the stop list

	pStopIx = pDb->pIxStopList;
	pDb->pIxStopList = NULL;
	for( ; pStopIx; pStopIx = pNextIx)
	{
		pNextIx = pStopIx->pNext;
		f_free( &pStopIx);
	}

	// Start threads listed in the index start list.

	pStartIx = pDb->pIxStartList;
	pDb->pIxStartList = NULL;
	for( ; pStartIx; pStartIx = pNextIx)
	{
		pNextIx = pStartIx->pNext;
		(void)flmStartIndexBuild( pDb, pStartIx->indexStatus.uiIndexNum);
		f_free( &pStartIx);
	}
}	
Beispiel #3
0
int GEngine::gameLoop() {

	if (m_errorState == true) {
		setErrorDetails(
				"Cannot enter game loop when the engine is in a error state");
		return -1;
	}

	m_GameActive = true;
	m_physicsActive = true;

	m_clock.initialize();
	float secondsPassed;

	while (m_GameActive) {

		secondsPassed = m_clock.calculateElapsedTime();

		//Alert to the FPS calculator of a new frame
		m_fpsCalculator.frame(secondsPassed);

		//Run through the postbox and trigger events
		while (m_inputPostbox.HasNext()) {

			processEvent(m_inputPostbox.ReadNext());

		}

		doFrameUpdate(secondsPassed);

		//Rendering Step

		GRenderer::getRenderer()->renderScene(m_canvasWidth, m_canvasHeight);

		//End of rendering step

		//Sleep
		f_sleep();
	}

	return 0;
}
/****************************************************************************
Desc: This routine functions as a thread.  It monitors open files and
		frees up files which have been closed longer than the maximum
		close time.
****************************************************************************/
FSTATIC RCODE XFLAPI flmCPThread(
	IF_Thread *		pThread)
{
	CP_INFO *			pCPInfo = (CP_INFO *)pThread->getParm1();
	F_Database *		pDatabase = pCPInfo->pDatabase;

	pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING);
	for (;;)
	{
		f_sleep( 1000);
		if (pDatabase->tryCheckpoint( pThread, pCPInfo))
		{
			break;
		}
	}

	pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING);

	flmFreeCPInfo( &pCPInfo);
	return( NE_XFLM_OK);
}
FINLINE int _sema_timedwait(
	sema_t *			pSem,
	unsigned int	msecs)
{
	int					iErr = 0;

   // If timeout is F_WAITFOREVER, do sem_wait.

   if( msecs == F_WAITFOREVER)
   {
      iErr = _sema_wait( pSem);
      return( iErr);
   }

	for( ;;)
	{
		if( (iErr = sema_trywait( pSem)) != 0)
		{
			if( iErr == EINTR)
			{
				iErr = 0;
			}

			f_sleep( f_min( msecs, 10));
			msecs -= f_min( msecs, 10);

			if( !msecs)
			{
				iErr = -1;
				goto Exit;
			}

			continue;
		}
	}

Exit:

	return( iErr);
}
/****************************************************************************
Desc:	Thread that will delete block chains from deleted indexes and
		tables in the background.
****************************************************************************/
RCODE SQFAPI F_Database::maintenanceThread(
	IF_Thread *		pThread)
{
	RCODE					rc = NE_SFLM_OK;
	F_Database *		pDatabase = (F_Database *)pThread->getParm1();
	F_Db *				pDb;
	F_Row *				pRow;
	FLMUINT64			ui64MaintRowId;
	FLMBOOL				bStartedTrans;
	FLMBOOL				bShutdown;
	F_DbSystem *		pDbSystem;
	FSTableCursor *	pTableCursor;
	FLMUINT				uiBlkAddress;
	FLMBOOL				bIsNull;
	FLMUINT				uiBlocksToFree;
	FLMUINT				uiBlocksFreed;

Retry:
	
	rc = NE_SFLM_OK;
	pDb = NULL;
	pRow = NULL;
	bStartedTrans = FALSE;
	bShutdown = FALSE;
	pDbSystem = NULL;
	pTableCursor = NULL;

	if( (pDbSystem = f_new F_DbSystem) == NULL)
	{
		rc = RC_SET( NE_SFLM_MEM);
		goto Exit;
	}

	pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING);

	if( RC_BAD( rc = pDbSystem->internalDbOpen( pDatabase, &pDb)))
	{
		// If the file is being closed, this is not an error.

		if( pDatabase->getFlags() & DBF_BEING_CLOSED)
		{
			rc = NE_SFLM_OK;
			bShutdown = TRUE;
		}
		goto Exit;
	}
	pDbSystem->Release();
	pDbSystem = NULL;

	if ((pTableCursor = f_new FSTableCursor) == NULL)
	{
		rc = RC_SET( NE_SFLM_MEM);
		goto Exit;
	}

	for( ;;)
	{
		pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING);
		
		if( RC_BAD( rc = pDb->beginBackgroundTrans( pThread)))
		{
			goto Exit;
		}
		bStartedTrans = TRUE;
			
		pTableCursor->resetCursor();
		if (RC_BAD( rc = pTableCursor->setupRange( pDb, SFLM_TBLNUM_BLOCK_CHAINS,
												1, FLM_MAX_UINT64, FALSE)))
		{
			goto Exit;
		}
		
		// Free up to 25 blocks per transaction.
		
		uiBlocksToFree = 25;
		while (uiBlocksToFree)
		{

			if (RC_BAD( rc = pTableCursor->nextRow( pDb, &pRow, &ui64MaintRowId)))
			{
				if (rc != NE_SFLM_EOF_HIT)
				{
					RC_UNEXPECTED_ASSERT( rc);
					goto Exit;
				}
				rc = NE_SFLM_OK;
				break;
			}
			if (RC_BAD( rc = pRow->getUINT( pDb,
						SFLM_COLNUM_BLOCK_CHAINS_BLOCK_ADDRESS, &uiBlkAddress,
						&bIsNull)))
			{
				goto Exit;
			}
			if (bIsNull)
			{
				rc = RC_SET_AND_ASSERT( NE_SFLM_DATA_ERROR);
				goto Exit;
			}
			
			if( RC_BAD( rc = pDb->maintBlockChainFree(
				ui64MaintRowId, uiBlkAddress, uiBlocksToFree, 0, &uiBlocksFreed)))
			{
				goto Exit;
			}
			uiBlocksToFree -= uiBlocksFreed;
		}

		bStartedTrans = FALSE;
		if( RC_BAD( rc = pDb->commitTrans( 0, FALSE)))
		{
			goto Exit;
		}

		pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING);
		f_semWait( pDatabase->m_hMaintSem, F_WAITFOREVER);
			
		if (pThread->getShutdownFlag())
		{
			bShutdown = TRUE;
			goto Exit;
		}
	}

Exit:

	pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING);

	if (pDbSystem)
	{
		pDbSystem->Release();
	}
	
	if (pRow)
	{
		pRow->ReleaseRow();
	}

	if( bStartedTrans)
	{
		pDb->abortTrans();
	}

	if (pDb)
	{
		pDb->Release();
		pDb = NULL;
	}

	if (!bShutdown)
	{
		flmAssert( RC_BAD( rc));
		f_sleep( 250);
		f_semSignal( pDatabase->m_hMaintSem);
		goto Retry;
	}

	return( rc);
}
/****************************************************************************
Desc:		Stops a background indexing thread
Notes:	This routine DOES NOT assume that the global mutex is locked.  It
			will lock and unlock the mutex as needed.
****************************************************************************/
FSTATIC void stopBackgroundIndexThread(
	FDB *				pDb,
	FLMUINT			uiIndexNum,
	FLMBOOL			bWait,
	FLMBOOL *		pbStopped)
{
	F_BKGND_IX *	pBackgroundIx;
	FLMUINT			uiThreadId;
	FLMBOOL			bMutexLocked = FALSE;

	if( pbStopped)
	{
		*pbStopped = FALSE;
	}

	for( ;;)
	{
		// Lock the global mutex

		if( !bMutexLocked)
		{
			f_mutexLock( gv_FlmSysData.hShareMutex);
			bMutexLocked = TRUE;
		}

		// Get the background index

		if( (pBackgroundIx = flmBackgroundIndexGet( pDb->pFile,
			uiIndexNum, TRUE, &uiThreadId)) == NULL)
		{
			if( pbStopped)
			{
				*pbStopped = TRUE;
			}
			goto Exit;
		}

		// Set the thread's shutdown flag first.

		gv_FlmSysData.pThreadMgr->setThreadShutdownFlag( uiThreadId);

		// Unlock the global mutex

		f_mutexUnlock( gv_FlmSysData.hShareMutex);
		bMutexLocked = FALSE;

		// The thread may be waiting to start a transaction. 

		pDb->pFile->pFileLockObj->timeoutLockWaiter( uiThreadId);
		pDb->pFile->pWriteLockObj->timeoutLockWaiter( uiThreadId);
		
		if( !bWait)
		{
			break;
		}

		// Wait for the thread to terminate

		f_sleep( 50);
	}

Exit:

	if( bMutexLocked)
	{
		f_mutexUnlock( gv_FlmSysData.hShareMutex);
	}
}
bool TestExtMisc::test_sleep() {
  f_sleep(1);
  return Count(true);
}
/****************************************************************************
Desc:	Displays two lines of message and gets the user's input.
*****************************************************************************/
FLMUINT gigaGetInput(
	const char *	pszMsg1,
	const char *	pszMsg2,
	FLMBOOL			bMutexLocked)
{
	eColorType		eSaveBack;
	eColorType		eSaveFore;
	FLMUINT			uiChar;

	if (!bMutexLocked && gv_hWindowMutex != F_MUTEX_NULL)
	{
		f_mutexLock( gv_hWindowMutex);
	}

	// Get the background and foreground color so we can restore them.

	FTXWinGetBackFore( gv_pWindow, &eSaveBack, &eSaveFore);

	// Clear the last one or two lines on the screen

	if (pszMsg2 && *pszMsg2)
	{
		FTXWinClearXY( gv_pWindow, 0, gv_uiNumRows - 2);
	}
	else
	{
		FTXWinClearXY( gv_pWindow, 0, gv_uiNumRows - 1);
	}

	// Change to WHITE on RED.

	FTXWinSetBackFore( gv_pWindow, FLM_RED, FLM_WHITE);

	// Display messages on last two lines of screen.

	if (pszMsg2 && *pszMsg2)
	{
		FTXWinPrintStrXY( gv_pWindow, pszMsg1,
				0, gv_uiNumRows - 2);
		FTXWinPrintStrXY( gv_pWindow, pszMsg2,
				0, gv_uiNumRows - 1);
	}
	else
	{
		FTXWinPrintStrXY( gv_pWindow, pszMsg1,
				0, gv_uiNumRows - 1);
	}

	// Wait for user to press key.

	for (;;)
	{
		if (gv_bShutdown)
		{
			uiChar = 0;
			break;
		}
		if (RC_OK( FTXWinTestKB( gv_pWindow)))
		{
			FTXWinInputChar( gv_pWindow, &uiChar);
			break;
		}
		f_sleep(50);
	}

	// Clear out last one or two lines of screen.

	FTXWinSetBackFore( gv_pWindow, eSaveBack, eSaveFore);
	
	if (pszMsg2 && *pszMsg2)
	{
		FTXWinClearXY( gv_pWindow, 0, gv_uiNumRows - 2);
	}
	else
	{
		FTXWinClearXY( gv_pWindow, 0, gv_uiNumRows - 1);
	}

	if (!bMutexLocked && gv_hWindowMutex != F_MUTEX_NULL)
	{
		f_mutexUnlock( gv_hWindowMutex);
	}

	return( uiChar);
}
/****************************************************************************
Name:	flstMemoryManagerThread
Desc:	Thread that displays the current status of a database's cache
Note:	The caller must pass a valid share handle to the thread on startup.
*****************************************************************************/
RCODE FTKAPI flstMemoryManagerThread(
	IF_Thread *		pThread)
{
	RCODE					rc = NE_XFLM_OK;
	F_DynamicList *	pList = f_new F_DynamicList;
	FTX_SCREEN *		pScreen;
	FTX_WINDOW *		pTitleWin;
	FTX_WINDOW *		pListWin;
	FTX_WINDOW *		pHeaderWin;
	char					szTmpBuf[ 80];
	FLMUINT				uiLoop;
	FLMUINT				uiIteration = 0;
	FLMUINT				uiScreenCols;
	FLMUINT				uiScreenRows;
	XFLM_CACHE_INFO	CacheInfo;
	IF_DbSystem *		pDbSystem = NULL;

#define FMMT_TITLE_HEIGHT 1
#define FMMT_HEADER_HEIGHT 3

	if( RC_BAD( FTXScreenInit( "XFlaim Memory Manager", &pScreen)))
	{
		goto Exit;
	}

	FTXScreenGetSize( pScreen, &uiScreenCols, &uiScreenRows);
	FTXScreenDisplay( pScreen);

	if( RC_BAD( FTXWinInit( pScreen, 0, FMMT_TITLE_HEIGHT, &pTitleWin)))
	{
		goto Exit;
	}

	FTXWinPaintBackground( pTitleWin, FLM_RED);
	FTXWinPrintStr( pTitleWin, "XFlaim Memory Manager");
	FTXWinSetCursorType( pTitleWin, FLM_CURSOR_INVISIBLE);
	FTXWinOpen( pTitleWin);

	if( RC_BAD( FTXWinInit( pScreen, uiScreenCols, FMMT_HEADER_HEIGHT,
		&pHeaderWin)))
	{
		goto Exit;
	}

	FTXWinSetBackFore( pHeaderWin, FLM_BLUE, FLM_WHITE);
	FTXWinClear( pHeaderWin);
	FTXWinPrintf( pHeaderWin,
	"\n                                    Block Cache     Node Cache");
	FTXWinSetCursorType( pHeaderWin, FLM_CURSOR_INVISIBLE);
	FTXWinSetScroll( pHeaderWin, FALSE);
	FTXWinSetLineWrap( pHeaderWin, FALSE);
	FTXWinMove( pHeaderWin, 0, FMMT_TITLE_HEIGHT);
	FTXWinOpen( pHeaderWin);

	if( RC_BAD( FTXWinInit( pScreen, uiScreenCols,
		uiScreenRows - FMMT_TITLE_HEIGHT - FMMT_HEADER_HEIGHT,
		&pListWin)))
	{
		goto Exit;
	}
	FTXWinMove( pListWin, 0, FMMT_TITLE_HEIGHT + FMMT_HEADER_HEIGHT);
	FTXWinOpen( pListWin);
	pList->setup( pListWin);
	
	if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem)))
	{
		goto Exit;
	}

	uiLoop = 0;
	while( !gv_bShutdown)
	{
		if( !(uiIteration % 100))
		{
			FLMUINT					uiKey = 0;
			XFLM_CACHE_USAGE *	pBlkCacheUse = &CacheInfo.BlockCache;
			XFLM_CACHE_USAGE *	pNodeCacheUse = &CacheInfo.NodeCache;

			pDbSystem->getCacheInfo( &CacheInfo);
			f_sprintf( szTmpBuf,
				"  Maximum Cache Bytes............... %10u",
				(unsigned)CacheInfo.uiMaxBytes);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Total Bytes Allocated ............ %10u",
				(unsigned)CacheInfo.uiTotalBytesAllocated);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Total Bytes....................... %10u     %10u",
				(unsigned)pBlkCacheUse->uiByteCount,
				(unsigned)pNodeCacheUse->uiByteCount);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Count ............................ %10u     %10u",
				(unsigned)pBlkCacheUse->uiCount,
				(unsigned)pNodeCacheUse->uiCount);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Cache Hits ....................... %10u     %10u",
				(unsigned)pBlkCacheUse->uiCacheHits,
				(unsigned)pNodeCacheUse->uiCacheHits);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Cache Hit Looks .................. %10u     %10u",
				(unsigned)pBlkCacheUse->uiCacheHitLooks,
				(unsigned)pNodeCacheUse->uiCacheHitLooks);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Cache Faults ..................... %10u     %10u",
				(unsigned)pBlkCacheUse->uiCacheFaults,
				(unsigned)pNodeCacheUse->uiCacheFaults);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Cache Fault Looks ................ %10u     %10u",
				(unsigned)pBlkCacheUse->uiCacheFaultLooks,
				(unsigned)pNodeCacheUse->uiCacheFaultLooks);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Dirty Count ...................... %10u",
				(unsigned)CacheInfo.uiDirtyCount);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Dirty Bytes ...................... %10u",
				(unsigned)CacheInfo.uiDirtyBytes);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  New Count ........................ %10u",
				(unsigned)CacheInfo.uiNewCount);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  New Bytes ........................ %10u",
				(unsigned)CacheInfo.uiNewBytes);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Log Count ........................ %10u",
				(unsigned)CacheInfo.uiLogCount);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Log Bytes ........................ %10u",
				(unsigned)CacheInfo.uiLogBytes);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Old Version Count ................ %10u     %10u",
				(unsigned)pBlkCacheUse->uiOldVerCount,
				(unsigned)pNodeCacheUse->uiOldVerCount);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Old Version Bytes ................ %10u     %10u",
				(unsigned)pBlkCacheUse->uiOldVerBytes,
				(unsigned)pNodeCacheUse->uiOldVerBytes);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Free Count ....................... %10u",
				(unsigned)CacheInfo.uiFreeCount);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Free Bytes ....................... %10u",
				(unsigned)CacheInfo.uiFreeBytes);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Replaceable Count ................ %10u",
				(unsigned)CacheInfo.uiReplaceableCount);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Replaceable Bytes ................ %10u",
				(unsigned)CacheInfo.uiReplaceableBytes);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Dynamic Cache Adjust .............        %s",
				(char *)(CacheInfo.bDynamicCacheAdjust ? "YES" : "NO"));
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Cache Adjust Percentage .......... %10u",
				(unsigned)CacheInfo.uiCacheAdjustPercent);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Cache Adjust Min ................. %10u",
				(unsigned)CacheInfo.uiCacheAdjustMin);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Cache Adjust Min To Leave ........ %10u",
				(unsigned)CacheInfo.uiCacheAdjustMinToLeave);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Slabs ............................ %10u     %10u",
				(unsigned)pBlkCacheUse->slabUsage.ui64Slabs,
				(unsigned)pNodeCacheUse->slabUsage.ui64Slabs);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Slab Bytes ....................... %10u     %10u",
				(unsigned)pBlkCacheUse->slabUsage.ui64SlabBytes,
				(unsigned)pNodeCacheUse->slabUsage.ui64SlabBytes);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Allocated Cells .................. %10u     %10u",
				(unsigned)pBlkCacheUse->slabUsage.ui64AllocatedCells,
				(unsigned)pNodeCacheUse->slabUsage.ui64AllocatedCells);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			f_sprintf( szTmpBuf,
				"  Free Cells ....................... %10u     %10u",
				(unsigned)pBlkCacheUse->slabUsage.ui64FreeCells,
				(unsigned)pNodeCacheUse->slabUsage.ui64FreeCells);
			pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf));

			pList->refresh();
		}

		if( RC_OK( FTXWinTestKB( pListWin)))
		{
			FLMUINT		uiChar;

			FTXWinInputChar( pListWin, &uiChar);
			switch( uiChar)
			{
				case 'R':
				case 'r':
					pDbSystem->resetStats();
					break;
				case FKB_UP:
					pList->cursorUp();
					break;
				case FKB_DOWN:
					pList->cursorDown();
					break;
				case FKB_PGUP:
					pList->pageUp();
					break;
				case FKB_PGDN:
					pList->pageDown();
					break;
				case FKB_HOME:
					pList->home();
					break;
				case FKB_END:
					pList->end();
					break;
				case FKB_ESCAPE:
					goto Exit;
			}
			pList->refresh();
		}

		if( pThread->getShutdownFlag())
		{
			break;
		}

		f_sleep( 10);
		uiIteration++;
	}

Exit:

	if( pList)
	{
		pList->Release();
	}

	if( pScreen)
	{
		FTXScreenFree( &pScreen);
	}
	
	if( pDbSystem)
	{
		pDbSystem->Release();
	}

	return( rc);
}
/****************************************************************************
Name:	flstIndexManagerThread
Desc:	Thread that displays the current status of all indexes in a database
Note:	The caller must open the database and pass a handle to the thread.
		The handle will be closed when the thread exits.
*****************************************************************************/
RCODE FTKAPI flstIndexManagerThread(
	IF_Thread *		pThread)
{
	RCODE						rc = NE_XFLM_OK;
	F_DynamicList *		pList = f_new F_DynamicList;
	FTX_WINDOW *			pTitleWin;
	FTX_WINDOW *			pListWin;
	FTX_WINDOW *			pHeaderWin;
	FTX_WINDOW *			pMsgWin;
	FLMUINT					uiIterations = 0;
	FLMUINT					uiScreenCols;
	FLMUINT					uiScreenRows;
	FLMUINT					uiIndex;
	FLMUINT					uiUpdateInterval;
	FLMUINT					uiLastUpdateTime;
	IX_DISPLAY_INFO		IxDispInfo;
	IX_DISPLAY_INFO *		pDispInfo;
	DLIST_NODE *			pTmpNd;
	FLMUINT					uiKey;
	FLMBOOL					bShowOnline = TRUE;
	F_Db *					pDb = (F_Db *)pThread->getParm1();
	FLMUINT					uiOneSec;
	FLMBOOL					bScreenLocked = FALSE;
	IX_Event					event;
	FLMBOOL					bRegisteredForEvent = FALSE;
	IF_DbSystem *			pDbSystem = NULL;

	event.setDispInfo( &IxDispInfo);

#define FIMT_TITLE_HEIGHT		1
#define FIMT_HEADER_HEIGHT		4
#define FIMT_LOG_HEIGHT			10

	f_memset( &IxDispInfo, 0, sizeof( IX_DISPLAY_INFO));
	IxDispInfo.hScreenMutex = F_MUTEX_NULL;
	IxDispInfo.pDb = (F_Db *)pDb;
	IxDispInfo.bShowTime = TRUE;

	if( RC_BAD( f_mutexCreate( &IxDispInfo.hScreenMutex)))
	{
		goto Exit;
	}

	if( RC_BAD( FTXScreenInit( "Index Manager", &IxDispInfo.pScreen)))
	{
		goto Exit;
	}

	FTXScreenGetSize( IxDispInfo.pScreen, &uiScreenCols, &uiScreenRows);
	FTXScreenDisplay( IxDispInfo.pScreen);

	if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, 0,
		FIMT_TITLE_HEIGHT, &pTitleWin)))
	{
		goto Exit;
	}

	FTXWinSetBackFore( pTitleWin, FLM_RED, FLM_WHITE);
	FTXWinClear( pTitleWin);
	FTXWinPrintStr( pTitleWin, "FLAIM Index Manager");
	FTXWinSetCursorType( pTitleWin, FLM_CURSOR_INVISIBLE);
	FTXWinOpen( pTitleWin);

	if( RC_BAD( FTXWinInit( IxDispInfo.pScreen,
		uiScreenCols, FIMT_HEADER_HEIGHT,
		&pHeaderWin)))
	{
		goto Exit;
	}

	FTXWinMove( pHeaderWin, 0, FIMT_TITLE_HEIGHT);
	FTXWinSetBackFore( pHeaderWin, FLM_BLUE, FLM_WHITE);
	FTXWinClear( pHeaderWin);
	FTXWinSetCursorType( pHeaderWin, FLM_CURSOR_INVISIBLE);
	FTXWinSetScroll( pHeaderWin, FALSE);
	FTXWinSetLineWrap( pHeaderWin, FALSE);
	FTXWinOpen( pHeaderWin);

	if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, uiScreenCols,
		uiScreenRows - FIMT_TITLE_HEIGHT - FIMT_HEADER_HEIGHT - FIMT_LOG_HEIGHT,
		&pListWin)))
	{
		goto Exit;
	}
	FTXWinMove( pListWin, 0, FIMT_TITLE_HEIGHT + FIMT_HEADER_HEIGHT);
	FTXWinOpen( pListWin);
	pList->setup( pListWin);

	if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, uiScreenCols, FIMT_LOG_HEIGHT,
		&IxDispInfo.pLogWin)))
	{
		goto Exit;
	}

	FTXWinDrawBorder( IxDispInfo.pLogWin);
	FTXWinMove( IxDispInfo.pLogWin, 0, uiScreenRows - FIMT_LOG_HEIGHT);
	FTXWinSetBackFore( IxDispInfo.pLogWin, FLM_BLUE, FLM_WHITE);
	FTXWinClear( IxDispInfo.pLogWin);
	FTXWinSetCursorType( IxDispInfo.pLogWin, FLM_CURSOR_INVISIBLE);
	FTXWinSetScroll( IxDispInfo.pLogWin, TRUE);
	FTXWinSetLineWrap( IxDispInfo.pLogWin, FALSE);
	FTXWinOpen( IxDispInfo.pLogWin);
	
	if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem)))
	{
		goto Exit;
	}

	if (RC_BAD( rc = pDbSystem->registerForEvent(
		XFLM_EVENT_UPDATES, &event)))
	{
		goto Exit;
	}
	bRegisteredForEvent = TRUE;

	FTXWinSetFocus( pListWin);
	uiIterations = 0;
	uiUpdateInterval = FLM_SECS_TO_TIMER_UNITS( 1);
	uiOneSec = FLM_SECS_TO_TIMER_UNITS( 1);
	uiLastUpdateTime = 0;
	while( !gv_bShutdown)
	{
		FLMUINT	uiCurrTime = FLM_GET_TIMER();

		if( bScreenLocked)
		{
			f_mutexUnlock( IxDispInfo.hScreenMutex);
			bScreenLocked = FALSE;
		}

		if( FLM_ELAPSED_TIME( uiCurrTime, uiLastUpdateTime) >= uiUpdateInterval)
		{
Update_Screen:

			if( !bScreenLocked)
			{
				f_mutexLock( IxDispInfo.hScreenMutex);
				bScreenLocked = TRUE;
			}

			FTXWinSetCursorPos( pHeaderWin, 0, 1);
			if( IxDispInfo.bShowTime)
			{
				FTXWinPrintf( pHeaderWin, "Index Index           State Last       Rate       Keys       Documents  Time");
			}
			else
			{
				FTXWinPrintf( pHeaderWin, "Index Index           State Last       Rate       Keys       Documents  Trans");
			}
			FTXWinClearToEOL( pHeaderWin);
			FTXWinPrintf( pHeaderWin, "\n");
			FTXWinPrintf( pHeaderWin, "Num.  Name                  DOC");

			if (RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS)))
			{
				goto Exit;
			}

			pTmpNd = pList->getFirst();
			uiIndex = 0;
			for( ;;)
			{
				if( RC_BAD( pDb->indexGetNext( &uiIndex)))
				{
					break;
				}

				// Remove all invalid entries

				while( pTmpNd && pTmpNd->uiKey < uiIndex)
				{
					uiKey = pTmpNd->uiKey;
					pTmpNd = pTmpNd->pNext;
					pList->remove( uiKey);
				}

				if (RC_BAD( rc = pDb->indexStatus( uiIndex, &IxDispInfo.IndexStatus)))
				{
					goto Exit;
				}

				if( !bShowOnline &&
					IxDispInfo.IndexStatus.eState == XFLM_INDEX_ONLINE)
				{
					if( pTmpNd && pTmpNd->uiKey == uiIndex)
					{
						uiKey = pTmpNd->uiKey;
						pTmpNd = pTmpNd->pNext;
						pList->remove( uiKey);
					}
					continue;
				}

				if( pTmpNd && pTmpNd->uiKey == uiIndex)
				{
					FLMUINT	uiOldest;
					FLMUINT	uiElapsed;

					pDispInfo = (IX_DISPLAY_INFO *)pTmpNd->pvData;
					f_strcpy( IxDispInfo.szName, pDispInfo->szName);

					// Copy the saved information.

					f_memcpy( &IxDispInfo.ui64SaveDocsProcessed [0],
								&pDispInfo->ui64SaveDocsProcessed [0],
								sizeof( FLMUINT) * MAX_VALS_TO_SAVE);
					f_memcpy( &IxDispInfo.uiDocSaveTime [0],
								&pDispInfo->uiDocSaveTime [0],
								sizeof( FLMUINT) * MAX_VALS_TO_SAVE);
					uiOldest = IxDispInfo.uiOldestSaved = pDispInfo->uiOldestSaved;

					// Recalculate the indexing rate.

					uiCurrTime = FLM_GET_TIMER();
					uiElapsed = (uiCurrTime - IxDispInfo.uiDocSaveTime [uiOldest]) /
															uiOneSec;
					if (uiElapsed && IxDispInfo.IndexStatus.ui64DocumentsProcessed)
					{
						if( IxDispInfo.ui64SaveDocsProcessed[ uiOldest] <
							IxDispInfo.IndexStatus.ui64DocumentsProcessed)
						{
							IxDispInfo.uiIndexingRate =
										// Records processed in time period
										(FLMUINT)((IxDispInfo.IndexStatus.ui64DocumentsProcessed -
										 IxDispInfo.ui64SaveDocsProcessed [uiOldest]) / uiElapsed);
						}
						else
						{
							IxDispInfo.uiIndexingRate = 0;
						}
					}
					else
					{
						IxDispInfo.uiIndexingRate = 0;
					}

					// Overwrite the oldest with the current data.

					IxDispInfo.uiDocSaveTime [uiOldest] = uiCurrTime;
					IxDispInfo.ui64SaveDocsProcessed [uiOldest] =
							IxDispInfo.IndexStatus.ui64DocumentsProcessed;

					// Move oldest pointer for next update.

					if (++IxDispInfo.uiOldestSaved == MAX_VALS_TO_SAVE)
					{
						IxDispInfo.uiOldestSaved = 0;
					}
				}
				else
				{
					FLMUINT			uiLoop;
					FLMUINT			uiBufLen;
					F_DataVector	srchKey;

					uiCurrTime = FLM_GET_TIMER();
					IxDispInfo.uiIndexingRate = 0;
					for (uiLoop = 0; uiLoop < MAX_VALS_TO_SAVE; uiLoop++)
					{
						IxDispInfo.ui64SaveDocsProcessed [uiLoop] =
								IxDispInfo.IndexStatus.ui64DocumentsProcessed;
						IxDispInfo.uiDocSaveTime [uiLoop] = uiCurrTime;
					}
					IxDispInfo.uiOldestSaved = 0;

					// Retrieve index name

					if (RC_BAD( srchKey.setUINT( 0, ELM_INDEX_TAG)))
					{
						break;
					}
					if (RC_BAD( srchKey.setUINT( 1, uiIndex)))
					{
						break;
					}

					if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX,
											&srchKey, XFLM_EXACT, &srchKey)))
					{
						if (rc != NE_XFLM_NOT_FOUND)
						{
							break;
						}
					}
					else
					{
						F_DOMNode *	pNode = NULL;

						if (RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION,
													srchKey.getDocumentID(), &pNode)))
						{
							if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
							{
								break;
							}
						}
						else
						{
							uiBufLen = sizeof( IxDispInfo.szName);
							rc = pNode->getAttributeValueUTF8( pDb,
									ATTR_NAME_TAG, (FLMBYTE *)IxDispInfo.szName, uiBufLen);
							pNode->Release();
							if (rc != NE_XFLM_OK &&
								 rc != NE_XFLM_DOM_NODE_NOT_FOUND &&
								 rc != NE_XFLM_CONV_DEST_OVERFLOW)
							{
								break;
							}
						}
					}
				}

				pList->update( uiIndex, ixDisplayHook, &IxDispInfo, sizeof( IxDispInfo));
				pList->refresh();

				if( pTmpNd && pTmpNd->uiKey == uiIndex)
				{
					pTmpNd = pTmpNd->pNext;
				}
			}
			pDb->transAbort();
			uiLastUpdateTime = FLM_GET_TIMER();
			pList->refresh();
		}

		if( !bScreenLocked)
		{
			f_mutexLock( IxDispInfo.hScreenMutex);
			bScreenLocked = TRUE;
		}

		if( RC_OK( FTXWinTestKB( pListWin)))
		{
			FLMUINT		uiChar;

			FTXWinInputChar( pListWin, &uiChar);
			f_mutexUnlock( IxDispInfo.hScreenMutex);
			bScreenLocked = FALSE;

			switch( uiChar)
			{
				case 'O':
				case 'o':
				{
					bShowOnline = !bShowOnline;
					goto Update_Screen;
				}

				case '+':
				case 'r':
				{
					if( (pTmpNd = pList->getCurrent()) != NULL)
					{
						if (RC_BAD( rc = pDb->indexResume( pTmpNd->uiKey)))
						{
							goto Exit;
						}
						goto Update_Screen;
					}
					break;
				}

				case 's':
				{
					if( (pTmpNd = pList->getCurrent()) != NULL)
					{
						if (RC_BAD( rc = pDb->indexSuspend( pTmpNd->uiKey)))
						{
							goto Exit;
						}
						goto Update_Screen;
					}
					break;
				}

				case FKB_ALT_S:
				case 'S':
				{
					f_mutexLock( IxDispInfo.hScreenMutex);
					FTXMessageWindow( IxDispInfo.pScreen, FLM_RED, FLM_WHITE,
								"Suspending all indexes ....",
								NULL, &pMsgWin);

					f_mutexUnlock( IxDispInfo.hScreenMutex);

					if (RC_OK( pDb->transBegin( XFLM_UPDATE_TRANS)))
					{
						uiIndex = 0;
						for( ;;)
						{
							if( RC_BAD( pDb->indexGetNext( &uiIndex)))
							{
								break;
							}
							if (RC_BAD( pDb->indexSuspend( uiIndex)))
							{
								break;
							}
						}
						if (RC_BAD( pDb->transCommit()))
						{
							(void)pDb->transAbort();
						}
					}

					if( pMsgWin)
					{
						f_mutexLock( IxDispInfo.hScreenMutex);
						FTXWinFree( &pMsgWin);
						f_mutexUnlock( IxDispInfo.hScreenMutex);
					}
					goto Update_Screen;
				}

				case 'R':
				case FKB_ALT_R:
				{
					f_mutexLock( IxDispInfo.hScreenMutex);
					FTXMessageWindow( IxDispInfo.pScreen, FLM_RED, FLM_WHITE,
						"Resuming all indexes                                ",
						NULL,
						&pMsgWin);
					f_mutexUnlock( IxDispInfo.hScreenMutex);

					if (RC_OK( pDb->transBegin( XFLM_UPDATE_TRANS)))
					{
						uiIndex = 0;
						for( ;;)
						{
							if( RC_BAD( pDb->indexGetNext( &uiIndex)))
							{
								break;
							}

							if (RC_BAD( pDb->indexResume( uiIndex)))
							{
								break;
							}
						}
						if (RC_BAD( pDb->transCommit()))
						{
							(void)pDb->transAbort();
							break;
						}
					}
					if( pMsgWin)
					{
						f_mutexLock( IxDispInfo.hScreenMutex);
						FTXWinFree( &pMsgWin);
						f_mutexUnlock( IxDispInfo.hScreenMutex);
					}
					goto Update_Screen;
				}

				case 'T':
				case 't':
				{
					IxDispInfo.bShowTime = !IxDispInfo.bShowTime;
					goto Update_Screen;
				}

				case '?':
				{
					FTX_WINDOW *		pHelpWin = NULL;
					FTX_WINDOW *		pHelpTitle = NULL;
					F_DynamicList *	pHelpList = NULL;
					FLMUINT				uiItem = 0;
					char					szTmpBuf [100];

					f_mutexLock( IxDispInfo.hScreenMutex);
					bScreenLocked = TRUE;

					if( (pHelpList = f_new F_DynamicList) == NULL)
					{
						goto Help_Exit;
					}

					if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, uiScreenCols,
						1, &pHelpTitle)))
					{
						goto Help_Exit;
					}

					FTXWinSetBackFore( pHelpTitle, FLM_RED, FLM_WHITE);
					FTXWinClear( pHelpTitle);
					FTXWinSetCursorType( pHelpTitle, FLM_CURSOR_INVISIBLE);
					FTXWinSetScroll( pHelpTitle, FALSE);
					FTXWinSetLineWrap( pHelpTitle, FALSE);
					FTXWinPrintf( pHelpTitle, "FLAIM Index Manager - Help");
					FTXWinOpen( pHelpTitle);

					if( RC_BAD( FTXWinInit( IxDispInfo.pScreen, uiScreenCols,
						uiScreenRows - 1, &pHelpWin)))
					{
						goto Help_Exit;
					}
					FTXWinDrawBorder( pHelpWin);
					FTXWinOpen( pHelpWin);
					pHelpList->setup( pHelpWin);

					f_sprintf( szTmpBuf, "R, ALT_R  Resume all indexes");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					f_sprintf( szTmpBuf, "S, ALT_S  Suspend all indexes");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					f_sprintf( szTmpBuf, "o, O      Toggle display of on-line indexes");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					f_sprintf( szTmpBuf, "+, r      Resume selected index with auto on-line option");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					f_sprintf( szTmpBuf, "s         Suspend selected index");
					pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf));

					pHelpList->refresh();
					pHelpWin = pHelpList->getListWin();

					f_mutexUnlock( IxDispInfo.hScreenMutex);
					bScreenLocked = FALSE;

					while( !gv_bShutdown)
					{
						f_mutexLock( IxDispInfo.hScreenMutex);
						bScreenLocked = TRUE;

						if( RC_OK( FTXWinTestKB( pHelpWin)))
						{
							FLMUINT		uiTmpChar;
							
							FTXWinInputChar( pHelpWin, &uiTmpChar);
							if( uiTmpChar == FKB_ESCAPE)
							{
								break;
							}
							pHelpList->defaultKeyAction( uiTmpChar);
						}

						f_mutexUnlock( IxDispInfo.hScreenMutex);
						bScreenLocked = FALSE;
						f_sleep( 10);
					}

Help_Exit:
					if( !bScreenLocked)
					{
						f_mutexLock( IxDispInfo.hScreenMutex);
						bScreenLocked = TRUE;
					}

					if( pHelpList)
					{
						pHelpList->Release();
					}

					if( pHelpTitle)
					{
						FTXWinFree( &pHelpTitle);
					}

					f_mutexUnlock( IxDispInfo.hScreenMutex);
					bScreenLocked = FALSE;
					break;
				}

				case FKB_ESCAPE:
				{
					goto Exit;
				}

				default:
				{
					f_mutexLock( IxDispInfo.hScreenMutex);
					pList->defaultKeyAction( uiChar);
					f_mutexUnlock( IxDispInfo.hScreenMutex);
					break;
				}
			}
			f_mutexLock( IxDispInfo.hScreenMutex);
			pList->refresh();
			f_mutexUnlock( IxDispInfo.hScreenMutex);
		}

		uiIterations++;

		if( pThread->getShutdownFlag())
		{
			break;
		}

		f_sleep( 1);
	}

Exit:

	if( pList)
	{
		pList->Release();
	}

	if (bRegisteredForEvent)
	{
		pDbSystem->deregisterForEvent( XFLM_EVENT_UPDATES, &event);
	}

	if( IxDispInfo.pScreen)
	{
		FTXScreenFree( &IxDispInfo.pScreen);
	}

	if( IxDispInfo.hScreenMutex != F_MUTEX_NULL)
	{
		f_mutexDestroy( &IxDispInfo.hScreenMutex);
	}
	
	if( pDb != NULL)
	{
		pDb->Release();
	}
	
	if( pDbSystem)
	{
		pDbSystem->Release();
	}

	return( rc);
}
/****************************************************************************
Name: UIMain
****************************************************************************/
void UIMain( void * pData)
{
	F_Db *			pDb = NULL;
	FTX_SCREEN *	pScreen = NULL;
	FTX_WINDOW *	pTitleWin = NULL;
	F_DomEditor	*	pDomEditor = NULL;
	char				szTitle[ 80];
	FLMUINT			uiDummy;
	char				szDbPath [F_PATH_MAX_SIZE];
	FLMUINT			Cols;
	FLMUINT			Rows;
	RCODE				rc;
	int				iResCode = 0;

	F_UNREFERENCED_PARM( pData);

	if( RC_BAD( dbSystem.init()))
	{
		iResCode = -1;
		goto Exit;
	}

	f_sprintf( szTitle,
		"DOMEdit for XFLAIM [DB=%s/BUILD=%s]",
		XFLM_CURRENT_VER_STR, __DATE__);

	if( RC_BAD( FTXInit( szTitle, 80, 50, FLM_BLUE, FLM_WHITE, NULL, NULL)))
	{
		iResCode = 1;
		goto Exit;
	}

	FTXSetShutdownFlag( gv_pFtxInfo, &gv_bShutdown);


	if( FTXScreenInit( gv_pFtxInfo, szTitle, &pScreen) != FTXRC_SUCCESS)
	{
		iResCode = 1;
		goto Exit;
	}

	if( FTXWinInit( pScreen, 0, 1, &pTitleWin) != FTXRC_SUCCESS)
	{
		iResCode = 1;
		goto Exit;
	}

	if( FTXWinPaintBackground( pTitleWin, FLM_RED) != FTXRC_SUCCESS)
	{
		iResCode = 1;
		goto Exit;
	}

	if( FTXWinPrintStr( pTitleWin, szTitle) != FTXRC_SUCCESS)
	{
		iResCode = 1;
		goto Exit;
	}

	FTXWinSetCursorType( pTitleWin, FLM_CURSOR_INVISIBLE);

	if( FTXWinOpen( pTitleWin) != FTXRC_SUCCESS)
	{
		iResCode = 1;
		goto Exit;
	}


	if( RC_BAD( pThreadMgr->createThread( &gv_pBackgroundThrd,
		_domEditBackgroundThread, "domedit_refresh")))
	{
		iResCode = 1;
		goto Exit;
	}

	/*
	Check expiration date
	*/

	if( RC_BAD( rc = domEditVerifyRun()))
	{
		FTXDisplayMessage( pScreen, FLM_RED, FLM_WHITE,
			"This Utility Has Expired",
			"NE_XFLM_ILLEGAL_OP", &uiDummy);
		f_sleep( 5000);
		iResCode = 1;
		goto Exit;
	}

	/*
	Open the database
	*/

	if( gv_szDbPath[ 0])
	{

		if( RC_BAD( rc = dbSystem.dbOpen( gv_szDbPath, NULL, gv_szRflDir,
			(IF_Db **)&pDb, gv_szPassword, gv_bAllowLimited)))
		{
			char	szErr [20];
			
			f_sprintf( szErr, "Error=0x%04X", (unsigned)rc);
			FTXDisplayMessage( pScreen, FLM_RED, FLM_WHITE,
				"Unable to open the database", szErr, &uiDummy);
			iResCode = 1;
			goto Exit;
		}
	}
	else
	{
		if( RC_BAD( rc = dbSystem.dbOpen( szDbPath, NULL, gv_szRflDir,
			(IF_Db **)&pDb, gv_szPassword, gv_bAllowLimited)))
		{
			char	szErr [20];
			
			f_sprintf( szErr, "Error=0x%04X", (unsigned)rc);
			FTXDisplayMessage( pScreen, FLM_RED, FLM_WHITE,
				"Unable to open the database", szErr, &uiDummy);
			iResCode = 1;
			goto Exit;
		}
		else
		{
			FTXWinClear( pTitleWin);
			if( FTXWinPrintf( pTitleWin, "%s (Direct)", szTitle) != FTXRC_SUCCESS)
			{
				iResCode = 1;
				goto Exit;
			}
		}
	}

	if( (pDomEditor = f_new F_DomEditor) == NULL)
	{
		iResCode = 1;
		goto Exit;
	}

	if( RC_BAD( pDomEditor->Setup( pScreen)))
	{
		iResCode = 1;
		goto Exit;
	}

	pDomEditor->setSource( pDb, XFLM_DATA_COLLECTION);
	pDomEditor->setShutdown( &gv_bShutdown);

	/*
	Fire up the editor
	*/

	FTXScreenGetSize( pScreen, &Cols, &Rows);
	pDomEditor->interactiveEdit( 0, 1, Cols - 1, Rows - 1);





Exit:

	if( pDomEditor)
	{
		pDomEditor->Release();
		pDomEditor = NULL;
	}

	gv_bShutdown = TRUE;
	
	if (pDb)
	{
		pDb->Release();
	}

	if( gv_pBackgroundThread)
	{
		gv_pBackgroundThrd->Release();
	}
	
	if( pThreadMgr)
	{
		pThreadMgr->Release();
	}

	dbSystem.exit();
}