/*  libatheos.so - the highlevel API library for AtheOS
 *  Copyright (C) 1999 - 2001  Kurt Skauen
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of version 2 of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 *  MA 02111-1307, USA
 */

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/stat.h>
#include <assert.h>

#include <atheos/time.h>

#include "directoryview.h"
#include <gui/window.h>
#include <gui/bitmap.h>
#include <gui/stringview.h>
#include <gui/button.h>
#include <gui/tableview.h>
#include <gui/requesters.h>
#include <gui/icon.h>
#include <gui/font.h>
#include <util/looper.h>
#include <util/message.h>
#include <storage/directory.h>
#include <storage/nodemonitor.h>

#include <list>
#include <set>
#include <sstream>

using namespace os;

namespace os_priv
{
	enum Error_e
	{ E_OK, E_SKIP, E_CANCEL, E_ALL };
	struct CopyFileParams_s
	{
		CopyFileParams_s( const std::vector < std::string > cDstPaths, const std::vector < std::string > cSrcPaths, const Messenger & cViewTarget ):m_cDstPaths( cDstPaths ), m_cSrcPaths( cSrcPaths ), m_cViewTarget( cViewTarget )
		{
		}

		std::vector < std::string > m_cDstPaths;
		  std::vector < std::string > m_cSrcPaths;
		Messenger m_cViewTarget;
	};

	struct DeleteFileParams_s
	{
		DeleteFileParams_s( const std::vector < std::string > cPaths, const Messenger & cViewTarget ):m_cPaths( cPaths ), m_cViewTarget( cViewTarget )
		{
		}
		std::vector < std::string > m_cPaths;
		Messenger m_cViewTarget;
	};

	class DirKeeper:public Looper
	{
	      public:
		enum
		{ M_CHANGE_DIR = 1, M_COPY_FILES, M_MOVE_FILES, M_DELETE_FILES, M_RENAME_FILES, M_ENTRIES_ADDED, M_ENTRIES_REMOVED, M_ENTRIES_UPDATED };
		  DirKeeper( const Messenger & cTarget, const std::string & cPath );
		virtual void HandleMessage( Message * pcMessage );
		virtual bool Idle();
	      private:
		void SendAddMsg( const std::string & cName );
		void SendRemoveMsg( const std::string & cName, dev_t nDevice, ino_t nInode );
		void SendUpdateMsg( const std::string & cName, dev_t nDevice, ino_t nInode );

		enum state_t
		{ S_IDLE, S_READDIR };
		Messenger m_cTarget;
		Directory *m_pcCurrentDir;
		NodeMonitor m_cMonitor;
		state_t m_eState;
		bool m_bWaitForAddReply;
		bool m_bWaitForRemoveReply;
		bool m_bWaitForUpdateReply;

		struct MonitoringFileNode;
		struct FileNode
		{
			FileNode( const std::string & cName, dev_t nDevice, ino_t nInode ):m_cName( cName ), m_nDevice( nDevice ), m_nInode( nInode )
			{
			}

			bool operator<( const FileNode & cNode ) const
			{
				return ( m_nInode == cNode.m_nInode ? ( m_nDevice < cNode.m_nDevice ) : ( m_nInode < cNode.m_nInode ) );
			}
			bool operator<( const MonitoringFileNode & cNode ) const;

			mutable std::string m_cName;
			dev_t m_nDevice;
			ino_t m_nInode;
		};
		struct MonitoringFileNode:public FileNode
		{
			MonitoringFileNode( const std::string & cName, dev_t nDevice, ino_t nInode ):FileNode( cName, nDevice, nInode )
			{
				m_pcMonitor = NULL;
			}
			
			~MonitoringFileNode() {
				if( m_pcMonitor ) {
					delete m_pcMonitor;
				}
			}
			
			mutable NodeMonitor* m_pcMonitor;
		};


		std::set < MonitoringFileNode > m_cFileMap;
		std::set < FileNode > m_cAddMap;
		std::set < FileNode > m_cUpdateMap;
		std::set < FileNode > m_cRemoveMap;
	};

	bool DirKeeper::FileNode::operator<( const DirKeeper::MonitoringFileNode & cNode ) const
	{
		return ( m_nInode == cNode.m_nInode ? ( m_nDevice < cNode.m_nDevice ) : ( m_nInode < cNode.m_nInode ) );
	}

}

using namespace os_priv;

bool load_icon( const char *pzPath, void *pBuffer, bool bLarge )
{
	IconDir sDir;
	IconHeader sHeader;

	FILE *hFile = fopen( pzPath, "r" );

	if( hFile == NULL )
	{
		printf( "Failed to open file %s\n", pzPath );
		return ( false );
	}

	if( fread( &sDir, sizeof( sDir ), 1, hFile ) != 1 )
	{
		printf( "Failed to read icon dir\n" );
	}
	if( sDir.nIconMagic != ICON_MAGIC )
	{
		printf( "Files %s is not an icon\n", pzPath );
		return ( false );
	}
	for( int i = 0; i < sDir.nNumImages; ++i )
	{
		if( fread( &sHeader, sizeof( sHeader ), 1, hFile ) != 1 )
		{
			printf( "Failed to read icon header\n" );
		}
		if( sHeader.nWidth == 32 )
		{
			if( bLarge )
			{
				fread( pBuffer, 32 * 32 * 4, 1, hFile );
			}
			else
			{
				fseek( hFile, 32 * 32 * 4, SEEK_CUR );
			}
		}
		else if( sHeader.nWidth == 16 )
		{
			if( bLarge == false )
			{
				fread( pBuffer, 16 * 16 * 4, 1, hFile );
			}
			else
			{
				fseek( hFile, 16 * 16 * 4, SEEK_CUR );
			}
		}
	}
	fclose( hFile );
	return ( true );
}


DirKeeper::DirKeeper( const Messenger & cTarget, const std::string & cPath ):Looper( "dir_worker" ), m_cTarget( cTarget )
{
	m_pcCurrentDir = NULL;
	try
	{
		m_pcCurrentDir = new Directory( cPath );
		m_cMonitor.SetTo( m_pcCurrentDir, NWATCH_DIR | NWATCH_NAME, this, this );
	} catch( std::exception & )
	{
	}
	if( m_pcCurrentDir != NULL )
	{
		m_eState = S_READDIR;
	}
	else
	{
		m_eState = S_IDLE;
	}
	m_bWaitForAddReply = false;
	m_bWaitForRemoveReply = false;
	m_bWaitForUpdateReply = false;
}

void DirKeeper::HandleMessage( Message * pcMessage )
{
	switch ( pcMessage->GetCode() )
	{
	case M_CHANGE_DIR:
		{
			String cNewPath;
			pcMessage->FindString( "path", &cNewPath );
			try
			{
				Directory *pcNewDir = new Directory( cNewPath );
				delete m_pcCurrentDir;

				m_pcCurrentDir = pcNewDir;
				m_eState = S_READDIR;
				m_cTarget.SendMessage( DirectoryView::M_CLEAR );
				m_cMonitor.SetTo( m_pcCurrentDir, NWATCH_DIR | NWATCH_NAME, this, this );
				m_cFileMap.clear();
				m_cAddMap.clear();
				m_cRemoveMap.clear();
				m_cUpdateMap.clear();
				m_bWaitForAddReply = false;
				m_bWaitForRemoveReply = false;
				m_bWaitForUpdateReply = false;
			}
			catch( std::exception & )
			{
			}
			break;
		}
	case M_ENTRIES_ADDED:
		{
			m_bWaitForAddReply = false;
			if( m_cAddMap.empty() == false )
			{
				Message cMsg( DirectoryView::M_ADD_ENTRY );
				int nCount = 0;

				while( m_cAddMap.empty() == false )
				{
					std::set < FileNode >::iterator i = m_cAddMap.begin();

					try
					{
						FSNode cNode( *m_pcCurrentDir, ( *i ).m_cName, O_RDONLY | O_NONBLOCK );
						struct stat sStat;

						cNode.GetStat( &sStat, false );
						uint32 anIcon[16 * 16];

						if( S_ISDIR( sStat.st_mode ) == false )
						{
							load_icon( "/system/icons/file.icon", anIcon, false );
						}
						else
						{
							load_icon( "/system/icons/folder.icon", anIcon, false );
						}
						cMsg.AddString( "name", ( *i ).m_cName );
						cMsg.AddData( "stat", T_ANY_TYPE, &sStat, sizeof( sStat ) );
						cMsg.AddData( "small_icon", T_ANY_TYPE, anIcon, 16 * 16 * 4 );

					}
					catch( std::exception & )
					{
					}
					m_cAddMap.erase( i );
					if( ++nCount > 5 )
					{
						break;
					}
				}
				m_bWaitForAddReply = true;
				m_cTarget.SendMessage( &cMsg );
			}
			break;
		}
	case M_ENTRIES_UPDATED:
		{
			m_bWaitForUpdateReply = false;
			if( m_cUpdateMap.empty() == false )
			{
				Message cMsg( DirectoryView::M_UPDATE_ENTRY );
				int nCount = 0;

				while( m_cUpdateMap.empty() == false )
				{
					std::set < FileNode >::iterator i = m_cUpdateMap.begin();

					try
					{
						FSNode cNode( *m_pcCurrentDir, ( *i ).m_cName, O_RDONLY | O_NONBLOCK );
						struct stat sStat;

						cNode.GetStat( &sStat, false );
						uint32 anIcon[16 * 16];

						if( S_ISDIR( sStat.st_mode ) == false )
						{
							load_icon( "/system/icons/file.icon", anIcon, false );
						}
						else
						{
							load_icon( "/system/icons/folder.icon", anIcon, false );
						}
						cMsg.AddString( "name", ( *i ).m_cName );
						cMsg.AddData( "stat", T_ANY_TYPE, &sStat, sizeof( sStat ) );
						cMsg.AddData( "small_icon", T_ANY_TYPE, anIcon, 16 * 16 * 4 );

					}
					catch( std::exception & )
					{
					}
					m_cUpdateMap.erase( i );
					if( ++nCount > 5 )
					{
						break;
					}
				}
				m_bWaitForUpdateReply = true;
				m_cTarget.SendMessage( &cMsg );
			}
			break;
		}
	case M_ENTRIES_REMOVED:
		{
			m_bWaitForRemoveReply = false;
			if( m_cRemoveMap.empty() == false )
			{
				Message cMsg( DirectoryView::M_REMOVE_ENTRY );
				int nCount = 0;

				while( m_cRemoveMap.empty() == false )
				{
					std::set < FileNode >::iterator i = m_cRemoveMap.begin();

					cMsg.AddInt32( "device", ( *i ).m_nDevice );
					cMsg.AddInt64( "node", ( *i ).m_nInode );
					m_cRemoveMap.erase( i );
					if( ++nCount > 10 )
					{
						break;
					}
				}
				m_cTarget.SendMessage( &cMsg );
				m_bWaitForRemoveReply = true;
			}
			break;
		}
	case M_NODE_MONITOR:
		{
			int32 nEvent = -1;

			pcMessage->FindInt32( "event", &nEvent );
			switch ( nEvent )
			{
			case NWEVENT_CREATED:
				{
					std::string cName;

					pcMessage->FindString( "name", &cName );
					SendAddMsg( cName );
					break;
				}
			case NWEVENT_DELETED:
				{
					dev_t nDevice;
					int64 nInode;

					std::string cName;

					pcMessage->FindInt( "device", &nDevice );
					pcMessage->FindInt64( "node", &nInode );
					pcMessage->FindString( "name", &cName );
					SendRemoveMsg( cName, nDevice, nInode );
					break;
				}
			case NWEVENT_STAT_CHANGED:
				{
					dev_t nDevice;
					int64 nInode;

					std::string cName;

					pcMessage->FindInt( "device", &nDevice );
					pcMessage->FindInt64( "node", &nInode );
					pcMessage->FindString( "name", &cName );
					SendUpdateMsg( cName, nDevice, nInode );
					break;
				}
			case NWEVENT_MOVED:
				{
					dev_t nDevice;
					int64 nOldDir;
					int64 nNewDir;
					int64 nInode;

					std::string cName;

					pcMessage->FindInt( "device", &nDevice );
					pcMessage->FindInt64( "old_dir", &nOldDir );
					pcMessage->FindInt64( "new_dir", &nNewDir );
					pcMessage->FindInt64( "node", &nInode );
					pcMessage->FindString( "name", &cName );


					if( nInode == int64 ( m_pcCurrentDir->GetInode() ) )
					{
						printf( "%Ld moved from %Ld to %Ld\n", nInode, nOldDir, nNewDir );
					}
					else
					{
						if( nOldDir == nNewDir )
						{
							SendUpdateMsg( cName, nDevice, nInode );
						}
						else
						{
							if( nOldDir == int64 ( m_pcCurrentDir->GetInode() ) )
							{
								SendRemoveMsg( cName, nDevice, nInode );
							}
							else
							{
								SendAddMsg( cName );
							}
						}
					}
					break;
				}
			}
		}
	default:
		Looper::HandleMessage( pcMessage );
		break;
	}
}

void DirKeeper::SendUpdateMsg( const std::string & cName, dev_t nDevice, ino_t nInode )
{
	MonitoringFileNode cFileNode( cName, nDevice, nInode );
	std::set < MonitoringFileNode >::iterator i = m_cFileMap.find( cFileNode );

	if( i != m_cFileMap.end() )
	{
		try
		{
			if( cName.size() > 0 )
			{
				( *i ).m_cName = cName;
			}
			FSNode cNode( *m_pcCurrentDir, ( *i ).m_cName, O_RDONLY | O_NONBLOCK );
			struct stat sStat;

			cNode.GetStat( &sStat, false );

			FileNode cFileNode( ( *i ).m_cName, sStat.st_dev, sStat.st_ino );

//          std::set::iterator i = m_cRemoveMap.find( cFileNode );

			if( m_bWaitForUpdateReply )
			{
				m_cUpdateMap.insert( cFileNode );
			}
			else
			{
				uint32 anIcon[16 * 16];

				if( S_ISDIR( sStat.st_mode ) == false )
				{
					load_icon( "/system/icons/file.icon", anIcon, false );
				}
				else
				{
					load_icon( "/system/icons/folder.icon", anIcon, false );
				}

				Message cMsg( DirectoryView::M_UPDATE_ENTRY );

				cMsg.AddString( "name", ( *i ).m_cName );
				cMsg.AddData( "stat", T_ANY_TYPE, &sStat, sizeof( sStat ) );
				cMsg.AddData( "small_icon", T_ANY_TYPE, anIcon, 16 * 16 * 4 );

				m_cTarget.SendMessage( &cMsg );
				m_bWaitForUpdateReply = true;
			}
		}
		catch( std::exception & )
		{
		}
	}
	else
	{
		dbprintf( "Error: DirKeeper::SendUpdateMsg() got update message on non-existing node\n" );
	}

}

void DirKeeper::SendAddMsg( const std::string & cName )
{
	try
	{
		FSNode cNode( *m_pcCurrentDir, cName, O_RDONLY | O_NONBLOCK );
		struct stat sStat;

		cNode.GetStat( &sStat, false );

		FileNode cFileNode( cName, sStat.st_dev, sStat.st_ino );
		MonitoringFileNode cMFileNode( cName, sStat.st_dev, sStat.st_ino );

		std::set < FileNode >::iterator i = m_cRemoveMap.find( cFileNode );

		if( i != m_cRemoveMap.end() )
		{
			m_cRemoveMap.erase( i );
		}

		std::set < MonitoringFileNode >::iterator j = m_cFileMap.find( cMFileNode );

		if( j != m_cFileMap.end() )
		{
			SendUpdateMsg( cName, sStat.st_dev, sStat.st_ino );
			return;
		}
//      cMFileNode.m_cMonitor.SetTo( &cNode, NWATCH_STAT, this, this );
		( *m_cFileMap.insert( cMFileNode ).first ).m_pcMonitor = new NodeMonitor( &cNode, NWATCH_STAT, this, this );

		if( m_bWaitForAddReply )
		{
			m_cAddMap.insert( cFileNode );
		}
		else
		{
			uint32 anIcon[16 * 16];

			if( S_ISDIR( sStat.st_mode ) == false )
			{
				load_icon( "/system/icons/file.icon", anIcon, false );
			}
			else
			{
				load_icon( "/system/icons/folder.icon", anIcon, false );
			}

			Message cMsg( DirectoryView::M_ADD_ENTRY );

			cMsg.AddString( "name", cName );
			cMsg.AddData( "stat", T_ANY_TYPE, &sStat, sizeof( sStat ) );
			cMsg.AddData( "small_icon", T_ANY_TYPE, anIcon, 16 * 16 * 4 );

			m_cTarget.SendMessage( &cMsg );
			m_bWaitForAddReply = true;
		}
	}
	catch( ... /*std::exception& */  )
	{
	}
}

void DirKeeper::SendRemoveMsg( const std::string & cName, dev_t nDevice, ino_t nInode )
{
	FileNode cFileNode( cName, nDevice, nInode );
	MonitoringFileNode cMFileNode( cName, nDevice, nInode );
	std::set < FileNode >::iterator i = m_cAddMap.find( cFileNode );

	if( i != m_cAddMap.end() )
	{
		m_cAddMap.erase( i );
		return;
	}
	std::set < MonitoringFileNode >::iterator j = m_cFileMap.find( cMFileNode );

	if( j != m_cFileMap.end() )
	{
		m_cFileMap.erase( j );

		if( m_bWaitForRemoveReply )
		{
			m_cRemoveMap.insert( cFileNode );
		}
		else
		{
			Message cMsg( DirectoryView::M_REMOVE_ENTRY );

			cMsg.AddInt32( "device", nDevice );
			cMsg.AddInt64( "node", nInode );
			m_cTarget.SendMessage( &cMsg );
			m_bWaitForRemoveReply = true;
		}
	}
}

bool DirKeeper::Idle()
{
	switch ( m_eState )
	{
	case S_READDIR:
		{
			if( m_pcCurrentDir == NULL )
			{
				m_eState = S_IDLE;
				return ( false );
			}
			String cName;
			if( m_pcCurrentDir->GetNextEntry( &cName ) == 1 )
			{
				if( ! (cName == ".") )
				{
					SendAddMsg( cName );
				}
				return ( true );
			}
			else
			{
				m_eState = S_IDLE;
				return ( false );
			}
			break;
		}
	case S_IDLE:
		return ( false );
	}
	return ( false );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

int ReadFileDlg( int nFile, void *pBuffer, int nSize, const char *pzPath, Error_e * peError )
{
      retry:
	int nLen = read( nFile, pBuffer, nSize );

	if( nLen >= 0 )
	{
		*peError = E_OK;
		return ( nLen );
	}
	else
	{
		if( errno == EINTR )
		{
			goto retry;
		}
	}
	std::stringstream cMsg;

	cMsg << "Failed to read from source file: " << pzPath << "\nError: " << strerror( errno );

	Alert *pcAlert = new Alert( "Error:",
		std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
		"Skip", "Retry", "Cancel", NULL );

	switch ( pcAlert->Go() )
	{
	case 0:
		*peError = E_SKIP;
		break;
	case 1:
		goto retry;
	case 2:
		*peError = E_CANCEL;
		break;
	default:
		*peError = E_SKIP;
		break;
	}
	return ( nLen );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

int WriteFileDlg( int nFile, const void *pBuffer, int nSize, const char *pzPath, Error_e * peError )
{
      retry:
	int nLen = write( nFile, pBuffer, nSize );

	if( nLen >= 0 )
	{
		*peError = E_OK;
		return ( nLen );
	}
	else
	{
		if( errno == EINTR )
		{
			goto retry;
		}
	}
	std::stringstream cMsg;
	cMsg << "Failed to write to destination file: " << pzPath << "\nError: " << strerror( errno );

	Alert *pcAlert = new Alert( "Error:",
		std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
		"Skip", "Retry", "Cancel", NULL );

	switch ( pcAlert->Go() )
	{
	case 0:
		*peError = E_SKIP;
		break;
	case 1:
		goto retry;
	case 2:
		*peError = E_CANCEL;
		break;
	default:
		*peError = E_SKIP;
		break;
	}
	return ( nLen );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

bool CopyFile( const char *pzDst, const char *pzSrc, bool *pbReplaceFiles, bool *pbDontOverwrite, ProgressRequester * pcProgress )
{
	struct stat sSrcStat;
	struct stat sDstStat;
	bool bDirExists = false;

	if( pcProgress->DoCancel() )
	{
		return ( false );
	}

	while( lstat( pzSrc, &sSrcStat ) < 0 )
	{
		std::stringstream cMsg;

		cMsg << "Failed to open source: " << pzSrc << "\nError: " << strerror( errno );
		Alert *pcAlert = new Alert( "Error:",
			std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
			"Skip", "Retry", "Cancel", NULL );

		switch ( pcAlert->Go() )
		{
		case 0:
			return ( true );
		case 1:
			break;
		case 2:
			return ( false );
		default:
			return ( true );
		}
	}

	bool bLoop = true;

	while( bLoop && lstat( pzDst, &sDstStat ) >= 0 )
	{
		if( sSrcStat.st_dev == sDstStat.st_dev && sSrcStat.st_ino == sDstStat.st_ino )
		{
			Alert *pcAlert = new Alert( "Error:", "Source and destination are the same, cant copy.", 0,
				"Ok", NULL );

			pcAlert->Go( NULL );
			return ( false );
		}

		if( S_ISDIR( sDstStat.st_mode ) )
		{
			if( S_ISDIR( sSrcStat.st_mode ) == false )
			{
				std::stringstream cMsg;

				cMsg << "Can't replace directory  " << pzDst << " with a file\n";

				Alert *pcAlert = new Alert( "Error:",
					std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
					"Skip", "Retry", "Cancel", NULL );

				switch ( pcAlert->Go() )
				{
				case 0:
					return ( true );
				case 1:
					break;
				case 2:
					return ( false );
				default:
					return ( true );
				}
			}
			else
			{
				bDirExists = true;
				break;
			}
		}
		else
		{
			if( *pbDontOverwrite )
			{
				return ( true );
			}
			if( *pbReplaceFiles )
			{
				unlink( pzDst );
				break;
			}
			std::stringstream cMsg;
			cMsg << "The destination file: " << pzDst << "already exists\nWould you like to replace it?";

			Alert *pcAlert = new Alert( "Error:",
				std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
				"Skip", "Skip all", "Replace", "Replace all", "Cancel", NULL );

			switch ( pcAlert->Go() )
			{
			case 0:	// Skip
				return ( true );
			case 1:	// Skip all
				*pbDontOverwrite = true;
				return ( true );
			case 2:	// Replace
				bLoop = false;
				unlink( pzDst );
				break;
			case 3:	// Replace all
				*pbReplaceFiles = true;
				bLoop = false;
				unlink( pzDst );
				break;
			case 4:	// Cancel
				return ( false );
			default:	// Bad
				return ( true );
			}
		}
	}

	if( S_ISDIR( sSrcStat.st_mode ) )
	{
		pcProgress->Lock();
		pcProgress->SetPathName( pzDst );
		pcProgress->Unlock();

		if( bDirExists == false )
		{
			while( mkdir( pzDst, sSrcStat.st_mode ) < 0 )
			{
				std::stringstream cMsg;

				cMsg << "Failed to create directory: " << pzDst << "\nError: " << strerror( errno );
				Alert *pcAlert = new Alert( "Error:",
					std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
					"Skip", "Retry", "Cancel", NULL );

				switch ( pcAlert->Go() )
				{
				case 0:
					return ( true );
				case 1:
					break;
				case 2:
					return ( false );
				default:
					return ( true );
				}
			}
		}
		DIR *pDir;

		for( ;; )
		{
			pDir = opendir( pzSrc );
			if( pDir != NULL )
			{
				break;
			}
			std::stringstream cMsg;
			cMsg << "Failed to open directory: " << pzSrc << "\nError: " << strerror( errno );
			Alert *pcAlert = new Alert( "Error:",
				std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
				"Skip", "Retry", "Cancel", NULL );

			switch ( pcAlert->Go() )
			{
			case 0:
				return ( true );
			case 1:
				break;
			case 2:
				return ( false );
			default:
				return ( true );
			}
		}

		struct dirent *psEntry;

		while( ( psEntry = readdir( pDir ) ) != NULL )
		{
			if( strcmp( psEntry->d_name, "." ) == 0 || strcmp( psEntry->d_name, ".." ) == 0 )
			{
				continue;
			}
			Path cSrcPath( pzSrc );
			Path cDstPath( pzDst );

			cSrcPath.Append( psEntry->d_name );
			cDstPath.Append( psEntry->d_name );

			if( CopyFile( cDstPath.GetPath().c_str(), cSrcPath.GetPath(  ).c_str(), pbReplaceFiles, pbDontOverwrite, pcProgress ) == false )
			{
				closedir( pDir );
				return ( false );
			}
		}
		closedir( pDir );
	}
	else if( S_ISLNK( sSrcStat.st_mode ) )
	{
		pcProgress->Lock();
		pcProgress->SetFileName( Path( pzDst ).GetLeaf() );
		pcProgress->Unlock();
		printf( "Copy link %s to %s\n", pzSrc, pzDst );
	}
	else
	{
		pcProgress->Lock();
		pcProgress->SetFileName( Path( pzDst ).GetLeaf() );
		pcProgress->Unlock();

		int nSrcFile = -1;
		int nDstFile = -1;

		// Workaround for bug in egcs-1.1.2 (gives a warning about using uninitialized variables)
		nSrcFile = -1;
		nDstFile = -1;

		for( ;; )
		{
			nSrcFile = open( pzSrc, O_RDONLY );
			if( nSrcFile >= 0 )
			{
				break;
			}
			std::stringstream cMsg;

			cMsg << "Failed to open source file: " << pzSrc << "\nError: " << strerror( errno );

			Alert *pcAlert = new Alert( "Error:",
				std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
				"Skip", "Retry", "Cancel", NULL );

			switch ( pcAlert->Go() )
			{
			case 0:
				return ( true );
			case 1:
				break;
			case 2:
				return ( false );
			default:
				return ( true );
			}
		}
		for( ;; )
		{
			nDstFile = open( pzDst, O_WRONLY | O_CREAT | O_TRUNC, sSrcStat.st_mode );
			if( nDstFile >= 0 )
			{
				break;
			}
			std::stringstream cMsg;

			cMsg << "Failed to create destination file: " << pzDst << "\nError: " << strerror( errno );

			Alert *pcAlert = new Alert( "Error:",
				std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
				"Skip", "Retry", "Cancel", NULL );

			switch ( pcAlert->Go() )
			{
			case 0:
				close( nSrcFile );
				return ( true );
			case 1:
				break;
			case 2:
				close( nSrcFile );
				return ( false );
			default:
				close( nSrcFile );
				return ( true );
			}
		}
		int nTotLen = 0;

		for( ;; )
		{
			char anBuffer[4096];
			int nLen;
			Error_e eError;

			if( pcProgress->DoSkip() )
			{
				close( nSrcFile );
				close( nDstFile );
				unlink( pzDst );
				return ( true );
			}
			for( ;; )
			{
				nLen = ReadFileDlg( nSrcFile, anBuffer, 4096, pzSrc, &eError );
				if( nLen >= 0 )
				{
					break;
				}
				close( nSrcFile );
				close( nDstFile );
				unlink( pzDst );

				if( eError == E_SKIP )
				{
					return ( true );
				}
				else if( eError == E_CANCEL )
				{
					return ( false );
				}
				else
				{
					assert( !"Invalid return code from ReadFileDlg()" );
					return ( false );
				}
			}
			for( ;; )
			{
				int nError = WriteFileDlg( nDstFile, anBuffer, nLen, pzDst, &eError );

				if( nError >= 0 )
				{
					break;
				}

				close( nSrcFile );
				close( nDstFile );
				unlink( pzDst );

				if( eError == E_SKIP )
				{
					return ( true );
				}
				else if( eError == E_CANCEL )
				{
					return ( false );
				}
				else
				{
					assert( !"Invalid return code from WriteFileDlg()" );
					return ( false );
				}
			}
			nTotLen += nLen;
			if( nLen < 4096 )
			{
				break;
			}
		}
		close( nSrcFile );
		close( nDstFile );

		if( nTotLen != sSrcStat.st_size )
		{
			std::stringstream cMsg;
			cMsg << "Mismatch between number of bytes read (" << nTotLen << ")\n" "and file size reported by file system (" << sSrcStat.st_size << ")!\n" "Seems like we might ended up corrupting the destination file\n";

			Alert *pcAlert = new Alert( "Warning:", cMsg.str(), 0, "Sorry", NULL );

			pcAlert->Go();
		}
//    printf( "Copy file %s to %s\n", pzSrc, pzDst );
	}
	return ( true );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

bool DeleteFile( const char *pzPath, ProgressRequester * pcProgress )
{
	struct stat sStat;

	if( pcProgress->DoCancel() )
	{
		return ( false );
	}

	while( lstat( pzPath, &sStat ) < 0 )
	{
		std::stringstream cMsg;

		cMsg << "Failed to stat file: " << pzPath << "\nError: " << strerror( errno );
		Alert *pcAlert = new Alert( "Error:",
			std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
			"Skip", "Retry", "Cancel", NULL );

		switch ( pcAlert->Go() )
		{
		case 0:
			return ( true );
		case 1:
			break;
		case 2:
			return ( false );
		default:
			return ( true );
		}
	}
	if( S_ISDIR( sStat.st_mode ) )
	{
		pcProgress->Lock();
		pcProgress->SetPathName( pzPath );
		pcProgress->Unlock();

		DIR *pDir;

		for( ;; )
		{
			pDir = opendir( pzPath );
			if( pDir != NULL )
			{
				break;
			}

			std::stringstream cMsg;

			cMsg << "Failed to open directory: " << pzPath << "\nError: " << strerror( errno );

			Alert *pcAlert = new Alert( "Error:",
				std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
				"Skip", "Retry", "Cancel", NULL );

			switch ( pcAlert->Go() )
			{
			case 0:
				return ( true );
			case 1:
				break;
			case 2:
				return ( false );
			default:
				return ( true );
			}
		}
		struct dirent *psEntry;

		std::list  cFileList;

		while( ( psEntry = readdir( pDir ) ) != NULL )
		{
			if( strcmp( psEntry->d_name, "." ) == 0 || strcmp( psEntry->d_name, ".." ) == 0 )
			{
				continue;
			}
			Path cPath( pzPath );

			cPath.Append( psEntry->d_name );
			cFileList.push_back( cPath.GetPath() );
		}
		closedir( pDir );
		while( cFileList.empty() == false )
		{
			std::list ::iterator i = cFileList.begin();

			if( DeleteFile( ( *i ).c_str(), pcProgress ) == false )
			{
				return ( false );
			}
			cFileList.erase( i );
		}
//    printf( "delete dir %s\n", pzPath );
		while( rmdir( pzPath ) < 0 )
		{
			std::stringstream cMsg;

			cMsg << "Failed to delete directory: " << pzPath << "\nError: " << strerror( errno );

			Alert *pcAlert = new Alert( "Error:",
				std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
				"Skip", "Retry", "Cancel", NULL );

			switch ( pcAlert->Go() )
			{
			case 0:
				return ( true );
			case 1:
				break;
			case 2:
				return ( false );
			default:
				return ( true );
			}
		}
	}
	else
	{
		pcProgress->Lock();
		pcProgress->SetFileName( Path( pzPath ).GetLeaf() );
		pcProgress->Unlock();
//    printf( "delete file %s\n", pzPath );
		while( unlink( pzPath ) < 0 )
		{
			std::stringstream cMsg;

			cMsg << "Failed to delete file: " << pzPath << "\nError: " << strerror( errno );

			Alert *pcAlert = new Alert( "Error:",
				std::string( cMsg.str(), 0, cMsg.str().length(  ) ), 0,
				"Skip", "Retry", "Cancel", NULL );

			switch ( pcAlert->Go() )
			{
			case 0:
				return ( true );
			case 1:
				break;
			case 2:
				return ( false );
			default:
				return ( true );
			}
		}
	}
	return ( true );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

int32 DelFileThread( void *pData )
{
	DeleteFileParams_s *psArgs = ( DeleteFileParams_s * ) pData;

	std::stringstream cMsg;
	const char *pzYes;
	const char *pzNo;

	if( psArgs->m_cPaths.size() == 1 )
	{
		struct stat sStat;

		if( lstat( psArgs->m_cPaths[0].c_str(), &sStat ) < 0 )
		{
			cMsg << "Failed to open file " << psArgs->m_cPaths[0].c_str() << "\nError: " << strerror( errno );
			Alert *pcAlert = new Alert( "Error:", cMsg.str(), 0, "Sorry", NULL );

			pcAlert->Go();
			delete psArgs;

			return ( 1 );
		}
		if( S_ISDIR( sStat.st_mode ) )
		{
			cMsg << "Are you sure you want to delete\n" "the directory \"" << Path( psArgs->m_cPaths[0].c_str() ).GetLeaf(  ).c_str() << "\"\n" "and all it's sub directories?";
		}
		else
		{
			cMsg << "Are you sure you want to delete the file\n" "\"" << Path( psArgs->m_cPaths[0].c_str() ).GetLeaf(  ).c_str() << "\"\n";
		}

		pzNo = "Leave it";
		pzYes = "Nuke it";
	}
	else
	{
		cMsg << "Are you sure you want to delete those " << psArgs->m_cPaths.size() << " entries?";
		pzNo = "Leave them";
		pzYes = "Nuke'em";
	}

	Alert *pcAlert = new Alert( "Verify file deletion:", cMsg.str(), 0, pzNo, pzYes, NULL );

	int nButton = pcAlert->Go();

	if( nButton != 1 )
	{
		delete psArgs;

		return ( 1 );
	}


	ProgressRequester *pcProgress = new ProgressRequester( Rect( 50, 20, 350, 150 ),
		"progress_window", "Delete file:", false );

	for( uint i = 0; i < psArgs->m_cPaths.size(); ++i )
	{
		if( DeleteFile( psArgs->m_cPaths[i].c_str(), pcProgress ) == false )
		{
			break;
		}
		Message cMsg( 125 );

		psArgs->m_cViewTarget.SendMessage( &cMsg );
	}

	pcProgress->Quit();

	delete psArgs;

	return ( 0 );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void StartFileDelete( const std::vector < std::string > &cPaths, const Messenger & cViewTarget )
{
	DeleteFileParams_s *psParams = new DeleteFileParams_s( cPaths, cViewTarget );
	thread_id hTread = spawn_thread( "delete_file_thread", (void*)DelFileThread, 0, 0, psParams );

	if( hTread >= 0 )
	{
		resume_thread( hTread );
	}
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

int32 CopyFileThread( void *pData )
{
	CopyFileParams_s *psParams = ( CopyFileParams_s * ) pData;

	ProgressRequester *pcProgress = new ProgressRequester( Rect( 50, 20, 350, 150 ),
		"progress_window", "Copy files:", true );

	for( uint i = 0; i < psParams->m_cSrcPaths.size(); ++i )
	{
		bool bReplaceFiles = false;
		bool bDontOverwrite = false;

		if( CopyFile( psParams->m_cDstPaths[i].c_str(), psParams->m_cSrcPaths[i].c_str(  ), &bReplaceFiles, &bDontOverwrite, pcProgress ) == false )
		{
			break;
		}

		Message cMsg( 125 );

		psParams->m_cViewTarget.SendMessage( &cMsg );
	}
	pcProgress->Quit();


	delete psParams;

	return ( 0 );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void StartFileCopy( const std::vector < std::string > &cDstPaths, const std::vector < std::string > &cSrcPaths, const Messenger & cViewTarget )
{
	CopyFileParams_s *psParams = new CopyFileParams_s( cDstPaths, cSrcPaths, cViewTarget );
	thread_id hTread = spawn_thread( "copy_file_thread", (void*)CopyFileThread, 0, 0, psParams );

	if( hTread >= 0 )
	{
		resume_thread( hTread );
	}
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

DirectoryView::State::State( ListView * pcView, const char *pzPath ):m_cPath( pzPath )
{
	for( uint i = 0; i < pcView->GetRowCount(); ++i )
	{
		if( pcView->IsSelected( i ) )
		{
			FileRow *pcRow = dynamic_cast < FileRow * >( pcView->GetRow( i ) );

			if( pcRow != NULL )
			{
				m_cSelection.push_back( pcRow->GetName() );
			}
		}
	}
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

DirectoryView::DirectoryView( const Rect & cFrame, const String & cPath, uint32 nModeFlags, uint32 nResizeMask, uint32 nViewFlags ):ListView( cFrame, "_list_view", nModeFlags, nResizeMask, nViewFlags ), m_cPath( cPath.c_str() )
{
	m_pcCurReadDirSession = NULL;

	InsertColumn( "", 16 );	// Icon
	InsertColumn( "Name", 150 );
	InsertColumn( "Size", 50 );
	InsertColumn( "Attr", 70 );
	InsertColumn( "Date", 70 );
	InsertColumn( "Time", 70 );

	m_pcDirChangeMsg = NULL;

	m_nLastKeyDownTime = 0;
	m_pcIconBitmap = new Bitmap( 16, 16, CS_RGB32 );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

DirectoryView::~DirectoryView()
{
//	delete m_pcIconBitmap;
}

void DirectoryView::AttachedToWindow()
{
	ListView::AttachedToWindow();
	m_pcDirKeeper = new DirKeeper( Messenger( this ), m_cPath.GetPath() );
	m_pcDirKeeper->Run();
//    ReRead();
}

void DirectoryView::DetachedFromWindow()
{
	m_pcDirKeeper->Quit();
	m_pcDirKeeper = NULL;
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::KeyDown( const char *pzString, const char *pzRawString, uint32 nQualifiers )
{
	char nChar = pzString[0];

	if( isprint( nChar ) )
	{
		bigtime_t nTime = get_system_time();

		if( nTime < m_nLastKeyDownTime + 1000000 )
		{
			m_cSearchString.append( &nChar, 1 );
		}
		else
		{
			m_cSearchString = std::string( &nChar, 1 );
		}
		for( uint i = 0; i < GetRowCount(); ++i )
		{
			FileRow *pcRow = dynamic_cast < FileRow * >( GetRow( i ) );

			if( pcRow != NULL )
			{
				if( m_cSearchString.compare( 0, m_cSearchString.size(), pcRow->m_cName ) == 0 )
//				if( m_cSearchString.compare( pcRow->m_cName, 0, m_cSearchString.size() ) == 0 )
				{
					Select( i );
					MakeVisible( i, false );
					break;
				}
			}
		}
		m_nLastKeyDownTime = nTime;
	}
	else
	{
		switch ( pzString[0] )
		{
		case VK_DELETE:
			{
				std::vector < std::string > cPaths;
				for( int i = GetFirstSelected(); i <= GetLastSelected(  ); ++i )
				{
					if( IsSelected( i ) )
					{
						FileRow *pcRow = dynamic_cast < FileRow * >( GetRow( i ) );

						if( pcRow != NULL )
						{
							Path cPath = m_cPath;

							cPath.Append( pcRow->m_cName.c_str() );
							cPaths.push_back( cPath.GetPath() );
						}
					}
				}
				StartFileDelete( cPaths, Messenger( this ) );
				break;
			}
		case VK_BACKSPACE:
			m_cPath.Append( ".." );
			ReRead();
			PopState();
			DirChanged( m_cPath.GetPath() );
			break;
		case VK_FUNCTION_KEY:
			{
				Looper *pcLooper = GetLooper();

				assert( pcLooper != NULL );
				Message *pcMsg = pcLooper->GetCurrentMessage();

				assert( pcMsg != NULL );
				int32 nKeyCode;

				if( pcMsg->FindInt32( "_raw_key", &nKeyCode ) != 0 )
				{
					return;
				}
				switch ( nKeyCode )
				{
				case 6:	// F5
					ReRead();
					break;
				}
				break;
			}
		default:
			ListView::KeyDown( pzString, pzRawString, nQualifiers );
			break;
		}
	}
}

int32 DirectoryView::ReadDirectory( void *pData )
{
	ReadDirParam *pcParam = ( ReadDirParam * ) pData;
	DirectoryView *pcView = pcParam->m_pcView;

	Window *pcWnd = pcView->GetWindow();

	if( pcWnd == NULL )
	{
		return ( 0 );
	}

	pcWnd->Lock();
	pcView->Clear();
	pcWnd->Unlock();

	DIR *pDir = opendir( pcView->GetPath().c_str(  ) );

	if( pDir == NULL )
	{
		dbprintf( "Error: DirectoryView::ReadDirectory() Failed to open %s\n", pcView->GetPath().c_str(  ) );
		goto error;
	}
	struct dirent *psEnt;

	while( pcParam->m_bCancel == false && ( psEnt = readdir( pDir ) ) != NULL )
	{
		struct stat sStat;

		if( strcmp( psEnt->d_name, "." ) == 0 /*|| strcmp( psEnt->d_name, ".." ) == 0 */  )
		{
			continue;
		}
		Path cFilePath( pcView->GetPath().c_str(  ) );

		cFilePath.Append( psEnt->d_name );

		stat( cFilePath.GetPath().c_str(), &sStat );

		FileRow *pcRow = new FileRow( pcView->m_pcIconBitmap, psEnt->d_name, sStat );

		if( S_ISDIR( pcRow->m_sStat.st_mode ) == false )
		{
			load_icon( "/system/icons/file.icon", pcRow->m_anIcon, false );
		}
		else
		{
			load_icon( "/system/icons/folder.icon", pcRow->m_anIcon, false );
		}
		pcWnd->Lock();
		pcView->InsertRow( pcRow );
		pcWnd->Unlock();
	}
	closedir( pDir );
      error:
	pcWnd->Lock();
	pcView->Sort();
	if( pcView->m_pcCurReadDirSession == pcParam )
	{
		pcView->m_pcCurReadDirSession = NULL;
	}
	pcWnd->Unlock();
	delete pcParam;

	return ( 0 );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::ReRead()
{
	Message cMsg( DirKeeper::M_CHANGE_DIR );

	cMsg.AddString( "path", m_cPath.GetPath() );
	m_pcDirKeeper->PostMessage( &cMsg );

/*    
    if ( m_pcCurReadDirSession != NULL ) {
	m_pcCurReadDirSession->m_bCancel = true;
    }
    m_pcCurReadDirSession = new ReadDirParam( this );
  
    thread_id hTread = spawn_thread( "read_dir_thread", ReadDirectory, 0, 0, m_pcCurReadDirSession );
    if ( hTread >= 0 ) {
	resume_thread( hTread );
    }*/
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

String DirectoryView::GetPath() const
{
	return ( m_cPath.GetPath() );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::SetPath( const String & cPath )
{
	while( m_cStack.size() > 0 )
	{
		m_cStack.pop();
	}
	m_cPath.SetTo( cPath.c_str() );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::Invoked( int nFirstRow, int nLastRow )
{
	if( nFirstRow != nLastRow )
	{
		ListView::Invoked( nFirstRow, nLastRow );
		return;
	}
	FileRow *pcRow = dynamic_cast < FileRow * >( GetRow( nFirstRow ) );

	if( pcRow == NULL )
	{
		ListView::Invoked( nFirstRow, nLastRow );
		return;
	}
	if( S_ISDIR( pcRow->m_sStat.st_mode ) == false )
	{
		ListView::Invoked( nFirstRow, nLastRow );
		return;
	}
	bool bBack = false;

	if( strcmp( pcRow->m_cName.c_str(), ".." ) == 0 )
	{
		m_cPath.Append( ".." );
		bBack = true;
	}
	else
	{
		m_cStack.push( State( this, m_cPath.GetPath().c_str() ) );
		m_cPath.Append( pcRow->m_cName.c_str() );
	}
	ReRead();

	if( bBack )
	{
		PopState();
	}
	DirChanged( m_cPath.GetPath() );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::PopState()
{
	if( m_cStack.empty() == false )
	{
		State & cState = m_cStack.top();

		for( uint i = 0; i < cState.m_cSelection.size(); ++i )
		{
			for( uint j = 0; j < GetRowCount(); ++j )
			{
				FileRow *pcRow = dynamic_cast < FileRow * >( GetRow( j ) );

				if( pcRow != NULL )
				{
					if( cState.m_cSelection[i] == pcRow->m_cName )
					{
						Select( j );
						MakeVisible( j );
						break;
					}
				}
			}
		}
		m_cStack.pop();
	}
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::SetDirChangeMsg( Message * pcMsg )
{
	delete m_pcDirChangeMsg;

	m_pcDirChangeMsg = pcMsg;
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::DirChanged( const String & cNewPath )
{
	if( m_pcDirChangeMsg != NULL )
	{
		Invoke( m_pcDirChangeMsg );
	}
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

bool DirectoryView::DragSelection( const Point & cPos )
{
	int nFirstSel = GetFirstSelected();
	int nLastSel = GetLastSelected();
	int nNumRows = nLastSel - nFirstSel + 1;

	float vRowHeight = GetRow( 0 )->GetHeight( this ) + 3.0f;

	float vSelWidth = 0;

	Message cData( 1234 );

	for( int i = nFirstSel; i <= nLastSel; ++i )
	{
		FileRow *pcRow = dynamic_cast < FileRow * >( GetRow( i ) );

		if( pcRow != NULL && IsSelected( i ) )
		{
			float vLen = GetStringWidth( pcRow->m_cName.c_str() );

			if( vLen > vSelWidth )
			{
				vSelWidth = vLen;
			}
			Path cPath = m_cPath;

			cPath.Append( pcRow->m_cName.c_str() );
			cData.AddString( "file/path", cPath.GetPath() );
		}
	}

	vSelWidth += 18.0f;
	Rect cSelRect( 0, 0, vSelWidth - 1.0f, vRowHeight * nNumRows - 1 );
	Point cHotSpot( cPos.x, cPos.y - GetRowPos( nFirstSel ) );

	if( nLastSel - nFirstSel < 4 )
	{
		Bitmap cImage( (int)(cSelRect.Width() + 1.0f), (int)(cSelRect.Height(  ) + 1.0f), CS_RGB32, Bitmap::ACCEPT_VIEWS );
		View *pcView = new View( cSelRect, "" );

		cImage.AddChild( pcView );
		pcView->SetFgColor( 255, 255, 255, 255 );
		pcView->FillRect( cSelRect );

		Rect cBmRect( 0, 0, 15, 15 );
		Rect cTextRect( 18, 0, vSelWidth - 1.0f, vRowHeight - 1.0f );

		pcView->SetBgColor( 100, 100, 100 );
		for( int i = nFirstSel; i <= nLastSel; ++i )
		{
			FileRow *pcRow = dynamic_cast < FileRow * >( GetRow( i ) );

			if( pcRow != NULL && IsSelected( i ) )
			{
				pcView->SetDrawingMode( DM_OVER );
				pcRow->Paint( cBmRect, pcView, 0, false, false, false );
				pcView->SetDrawingMode( DM_COPY );
				pcRow->Paint( cTextRect, pcView, 6, false, false, false );
			}
			cBmRect += Point( 0.0f, vRowHeight );
			cTextRect += Point( 0.0f, vRowHeight );
		}
		cImage.Sync();
		BeginDrag( &cData, cHotSpot, &cImage );
	}
	else
	{
		BeginDrag( &cData, cHotSpot, cSelRect );
	}

	return ( true );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::HandleMessage( Message * pcMessage )
{
	switch ( pcMessage->GetCode() )
	{
	case M_CLEAR:
		Clear();
		break;
	case M_ADD_ENTRY:
		{
//          std::string cName;
			const char *pzName;
			const struct stat * psStat;
			size_t nSize;
			int nCount = 0;

			pcMessage->GetNameInfo( "stat", NULL, &nCount );
			for( int i = 0; i < nCount; ++i )
			{
				if( pcMessage->FindString( "name", &pzName, i ) != 0 )
				{
					break;
				}
				if( pcMessage->FindData( "stat", T_ANY_TYPE, ( const void ** )&psStat, &nSize, i ) != 0 )
				{
					break;
				}
				try
				{
					FileRow *pcRow = new FileRow( m_pcIconBitmap, pzName, *psStat );

					const void *pIconData;

					if( pcMessage->FindData( "small_icon", T_ANY_TYPE, &pIconData, &nSize, i ) == 0 )
					{
						memcpy( pcRow->m_anIcon, pIconData, 16 * 16 * 4 );
					}
					InsertRow( pcRow );
				}
				catch( std::exception & )
				{
				}
			}
			m_pcDirKeeper->PostMessage( DirKeeper::M_ENTRIES_ADDED, m_pcDirKeeper, m_pcDirKeeper );
			break;
		}
	case M_UPDATE_ENTRY:
		{
			const char *pzName;
			const struct stat * psStat;
			size_t nSize;
			int nCount = 0;

			pcMessage->GetNameInfo( "stat", NULL, &nCount );
			for( int i = 0; i < nCount; ++i )
			{
				if( pcMessage->FindString( "name", &pzName, i ) != 0 )
				{
					break;
				}
				if( pcMessage->FindData( "stat", T_ANY_TYPE, ( const void ** )&psStat, &nSize, i ) != 0 )
				{
					break;
				}
				try
				{
					for( ListView::const_iterator j = begin(); j != end(  ); ++j )
					{
						FileRow *pcRow = static_cast < FileRow * >( *j );

						if( pcRow->m_sStat.st_ino == psStat->st_ino && pcRow->m_sStat.st_dev == psStat->st_dev )
						{
							const void *pIconData;

							pcRow->m_cName = pzName;
							pcRow->m_sStat = *psStat;
							if( pcMessage->FindData( "small_icon", T_ANY_TYPE, &pIconData, &nSize, i ) == 0 )
							{
								memcpy( pcRow->m_anIcon, pIconData, 16 * 16 * 4 );
								InvalidateRow( j - begin(), ListView::INV_VISUAL );
							}
							break;
						}
//                      RemoveRow( j - begin() );
//                      InsertRow( pcRow );
					}
				}
				catch( std::exception & )
				{
				}
			}
			m_pcDirKeeper->PostMessage( DirKeeper::M_ENTRIES_UPDATED, m_pcDirKeeper, m_pcDirKeeper );
			break;
		}
	case M_REMOVE_ENTRY:
		{
			dev_t nDevice;
			int64 nInode;

			int nCount = 0;

			pcMessage->GetNameInfo( "device", NULL, &nCount );

			for( int i = 0; i < nCount; ++i )
			{
				pcMessage->FindInt( "device", &nDevice, i );
				pcMessage->FindInt64( "node", &nInode, i );

				for( ListView::const_iterator j = begin(); j != end(  ); ++j )
				{
					FileRow *pcRow = static_cast < FileRow * >( *j );

					if( int64 ( pcRow->m_sStat.st_ino ) == nInode && pcRow->m_sStat.st_dev == nDevice )
					{
						delete RemoveRow( j - begin() );

						break;
					}
				}
			}
			m_pcDirKeeper->PostMessage( DirKeeper::M_ENTRIES_REMOVED, m_pcDirKeeper, m_pcDirKeeper );
			break;
		}
	case 125:
		ReRead();
	default:
		ListView::HandleMessage( pcMessage );
	}
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void FileRow::Paint( const Rect & cFrame, View * pcView, uint nColumn, bool bSelected, bool bHighlighted, bool bHasFocus )
{
	if( nColumn == 0 )
	{
		pcView->Sync();	// Make sure the previous icon is rendered before we overwrite the bitmap
		memcpy( m_pcIconBitmap->LockRaster(), m_anIcon, 16 * 16 * 4 );
//    ConvertIcon( m_pcIconBitmap, m_anIcon, false );
		pcView->DrawBitmap( m_pcIconBitmap, Rect( 0, 0, 15.0f, 15.0f ), cFrame );
		return;
	}

	font_height sHeight;

	pcView->GetFontHeight( &sHeight );

//  if ( bHighlighted ) {
//    pcView->SetFgColor( 0, 50, 200 );
//  } else if ( bSelected ) {
//    pcView->SetFgColor( 0, 0, 0 );
//  } else {
	pcView->SetFgColor( 255, 255, 255 );
//  }
	if( nColumn != 6 )
	{
		pcView->FillRect( cFrame );
	}

	float vFontHeight = sHeight.ascender + sHeight.descender;
	float vBaseLine = cFrame.top + ( cFrame.Height() + 1.0f ) / 2 - vFontHeight / 2 + sHeight.ascender;

	pcView->MovePenTo( cFrame.left + 3, vBaseLine );

	char zBuffer[256];
	const char *pzString = zBuffer;

	switch ( nColumn )
	{
	case 1:		// name
		pzString = m_cName.c_str();
		break;
	case 2:		// size
		if( S_ISDIR( m_sStat.st_mode ) )
		{
			strcpy( zBuffer, "" );
		}
		else
		{
			sprintf( zBuffer, "%Ld", m_sStat.st_size );
		}
		break;
	case 3:		// attributes
		for( int i = 0; i < 10; ++i )
		{
			if( m_sStat.st_mode & ( 1 << i ) )
			{
				zBuffer[i] = "drwxrwxrwx"[9 - i];
			}
			else
			{
				zBuffer[i] = '-';
			}
		}
		zBuffer[10] = '\0';
		break;
	case 4:		// date
		{
			time_t nTime = m_sStat.st_ctime;

			strftime( zBuffer, 256, "%d/%b/%Y", localtime( &nTime ) );
			break;
		}
	case 5:		// time
		{
			time_t nTime = m_sStat.st_ctime;

			strftime( zBuffer, 256, "%H:%M:%S", localtime( &nTime ) );
			break;
		}
	case 6:		// name (for drag image)
		pzString = m_cName.c_str();
		break;
	default:
		printf( "Error: FileRow::Paint() - Invalid column %d\n", nColumn );
		return;
	}

/*
  if ( bHighlighted || (bSelected && bHasFocus) ) {
  pcView->SetFgColor( 255, 255, 255 );
  } else {
  pcView->SetFgColor( 0, 0, 0 );
  }
  */
	if( bHighlighted && nColumn == 1 )
	{
		pcView->SetFgColor( 255, 255, 255 );
		pcView->SetBgColor( 0, 50, 200 );
	}
	else if( bSelected && nColumn == 1 )
	{
		pcView->SetFgColor( 255, 255, 255 );
		pcView->SetBgColor( 0, 0, 0 );
	}
	else
	{
		pcView->SetBgColor( 255, 255, 255 );
		pcView->SetFgColor( 0, 0, 0 );
	}
	if( bSelected && nColumn == 1 )
	{
		Rect cRect = cFrame;

		cRect.right = cRect.left + pcView->GetStringWidth( pzString ) + 4;
		cRect.top = vBaseLine - sHeight.ascender - 1;
		cRect.bottom = vBaseLine + sHeight.descender + 1;
		pcView->FillRect( cRect, Color32_s( 0, 0, 0, 0 ) );
	}
	pcView->DrawString( pzString );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::MouseUp( const Point & cPosition, uint32 nButtons, Message * pcData )
{
	Highlight( 0, GetRowCount() - 1, false, false );
	if( pcData == NULL )
	{
		ListView::MouseUp( cPosition, nButtons, NULL );
		return;
	}
	StopScroll();

	const char *pzPath;

	if( pcData->FindString( "file/path", &pzPath ) != 0 )
	{
		return;
	}
	if( m_cPath == Path( pzPath ).GetDir() )
	{
		return;
	}

	FileRow *pcRow;

	int nSel = HitTest( cPosition );

	if( nSel != -1 )
	{
		pcRow = dynamic_cast < FileRow * >( GetRow( nSel ) );
	}
	else
	{
		pcRow = NULL;
	}

	Path cDstDir;

	if( pcRow != NULL && S_ISDIR( pcRow->m_sStat.st_mode ) )
	{
		cDstDir = m_cPath;
		cDstDir.Append( pcRow->GetName().c_str(  ) );
	}
	else
	{
		cDstDir = m_cPath;
	}

	std::vector < std::string > cSrcPaths;
	std::vector < std::string > cDstPaths;
	for( int i = 0; pcData->FindString( "file/path", &pzPath, i ) == 0; ++i )
	{
		Path cSrcPath( pzPath );
		Path cDstPath = cDstDir;

		cDstPath.Append( cSrcPath.GetLeaf() );
		cSrcPaths.push_back( pzPath );
		cDstPaths.push_back( cDstPath.GetPath() );
	}
	printf( "Start copy\n" );
	StartFileCopy( cDstPaths, cSrcPaths, Messenger( this ) );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

void DirectoryView::MouseMove( const Point & cNewPos, int nCode, uint32 nButtons, Message * pcData )
{
	if( pcData == NULL )
	{
		ListView::MouseMove( cNewPos, nCode, nButtons, NULL );
		return;
	}
	if( nCode == MOUSE_OUTSIDE )
	{
		return;
	}
	if( nCode == MOUSE_EXITED )
	{
		Highlight( 0, GetRowCount() - 1, false, false );
		StopScroll();
		return;
	}

	const char *pzPath;

	if( pcData->FindString( "file/path", &pzPath ) != 0 )
	{
		return;
	}

	Rect cBounds = GetBounds();

	if( cNewPos.y < cBounds.top + AUTOSCROLL_BORDER )
	{
		StartScroll( SCROLL_DOWN, false );
	}
	else if( cNewPos.y > cBounds.bottom - AUTOSCROLL_BORDER )
	{
		StartScroll( SCROLL_UP, false );
	}
	else
	{
		StopScroll();
	}

	int nSel = HitTest( cNewPos );

	if( nSel != -1 )
	{
		FileRow *pcRow = dynamic_cast < FileRow * >( GetRow( nSel ) );

		if( pcRow != NULL && S_ISDIR( pcRow->m_sStat.st_mode ) )
		{
			Path cRowPath = m_cPath;

			cRowPath.Append( pcRow->GetName().c_str(  ) );
			if( !( cRowPath == Path( pzPath ) ) )
			{
				Highlight( nSel, true, true );
				return;
			}
		}
	}
	Highlight( 0, GetRowCount() - 1, false, false );
}

void FileRow::AttachToView( View * pcView, int nColumn )
{
	if( nColumn == 0 )
	{
		m_avWidths[0] = 16.0f;
		return;
	}

	char zBuffer[256];
	const char *pzString = zBuffer;

	switch ( nColumn )
	{
	case 1:		// name
		pzString = m_cName.c_str();
		break;
	case 2:		// size
		if( S_ISDIR( m_sStat.st_mode ) )
		{
			strcpy( zBuffer, "" );
		}
		else
		{
			sprintf( zBuffer, "%Ld", m_sStat.st_size );
		}
		break;
	case 3:		// attributes
		for( int i = 0; i < 10; ++i )
		{
			if( m_sStat.st_mode & ( 1 << i ) )
			{
				zBuffer[i] = "drwxrwxrwx"[9 - i];
			}
			else
			{
				zBuffer[i] = '-';
			}
		}
		zBuffer[10] = '\0';
		break;
	case 4:		// date
		{
			time_t nTime = m_sStat.st_ctime;

			strftime( zBuffer, 256, "%d/%b/%Y", localtime( &nTime ) );
			break;
		}
	case 5:		// time
		{
			time_t nTime = m_sStat.st_ctime;

			strftime( zBuffer, 256, "%H:%M:%S", localtime( &nTime ) );
			break;
		}
	case 6:		// name (for drag image)
		pzString = m_cName.c_str();
		break;
	default:
		printf( "Error: FileRow::AttachToView() - Invalid column %d\n", nColumn );
		return;
	}
	m_avWidths[nColumn] = pcView->GetStringWidth( pzString ) + 5.0f;
}

void FileRow::SetRect( const Rect & cRect, int nColumn )
{
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

float FileRow::GetWidth( View * pcView, int nColumn )
{
	return ( m_avWidths[nColumn] );

/*    if ( nColumn == 0 ) {
	return( 16.0f );
    }

    char  zBuffer[256];
    const char* pzString = zBuffer;
  
    switch( nColumn )
    {
	case 1:	// name
	    pzString = m_cName.c_str();
	    break;
	case 2:	// size
	    if ( S_ISDIR( m_sStat.st_mode ) ) {
		strcpy( zBuffer, "" );
	    } else {
		sprintf( zBuffer, "%Ld", m_sStat.st_size );
	    }
	    break;
	case 3:	// attributes
	    for ( int i = 0 ; i < 10 ; ++i ) {
		if ( m_sStat.st_mode & (1<GetStringWidth( pzString ) + 5.0f );*/
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

float FileRow::GetHeight( View * pcView )
{
	font_height sHeight;

	pcView->GetFontHeight( &sHeight );

	return ( std::max( 16.0f - 3.0f, sHeight.ascender + sHeight.descender ) );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

bool FileRow::HitTest( View * pcView, const Rect & cFrame, int nColumn, Point cPos )
{
	if( nColumn != 1 )
	{
		return ( true );
	}
	font_height sHeight;

	pcView->GetFontHeight( &sHeight );
	float nFontHeight = sHeight.ascender + sHeight.descender;
	float nBaseLine = cFrame.top + ( cFrame.Height() + 1.0f ) * 0.5f - nFontHeight / 2 + sHeight.ascender;
	Rect cRect( cFrame.left, nBaseLine - sHeight.ascender - 1.0f, cFrame.left + pcView->GetStringWidth( m_cName.c_str() ) + 4.0f, nBaseLine + sHeight.descender + 1.0f );

	return ( cRect.DoIntersect( cPos ) );
}

//----------------------------------------------------------------------------
// NAME:
// DESC:
// NOTE:
// SEE ALSO:
//----------------------------------------------------------------------------

bool FileRow::IsLessThan( const ListViewRow * pcOther, uint nColumn ) const
{
	const FileRow *pcRow = dynamic_cast < const FileRow * >( pcOther );

	if( NULL == pcRow )
	{
		return ( false );
	}

	if( S_ISDIR( m_sStat.st_mode ) != S_ISDIR( pcRow->m_sStat.st_mode ) )
	{
		return ( S_ISDIR( m_sStat.st_mode ) );
	}

	switch ( nColumn )
	{
	case 0:		// icon
	case 1:		// name
		return ( strcasecmp( m_cName.c_str(), pcRow->m_cName.c_str(  ) ) < 0 );
	case 2:		// size
		return ( m_sStat.st_size < pcRow->m_sStat.st_size );
	case 3:		// attributes
		return ( strcasecmp( m_cName.c_str(), pcRow->m_cName.c_str(  ) ) < 0 );
	case 4:		// date
	case 5:		// time
		return ( m_sStat.st_mtime < pcRow->m_sStat.st_mtime );
	default:
		printf( "Error: FileRow::IsLessThan() - Invalid column %d\n", nColumn );
		return ( false );
	}
}