// MyListCtrl.cpp: файл реализации
//

#include "stdafx.h"
#include "MyListCtrl.h"

// CMyListCtrl

IMPLEMENT_DYNAMIC(CMyListCtrl, CListCtrl)

CMyListCtrl::CMyListCtrl() :
	m_bkdImage(NULL),
	m_strCurrPath(_T("/")),     //корень диска.
	m_bSpecificColumn(FALSE),
	m_bCheckBinExtractStatus(FALSE),
	m_bCheckLogExtractStatus(FALSE)
{
}

CMyListCtrl::~CMyListCtrl()
{
	ClearImgVector();
}

BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CMyListCtrl::OnCustomDraw)
	ON_NOTIFY_REFLECT(NM_DBLCLK, &CMyListCtrl::OnNMDblclk)
	ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, &CMyListCtrl::OnLvnItemchanged)
	ON_WM_DESTROY()
	ON_WM_KEYDOWN()
	ON_WM_DROPFILES()
	ON_NOTIFY_REFLECT(NM_CLICK, &CMyListCtrl::OnNMClick)
	ON_NOTIFY_REFLECT(NM_RCLICK, &CMyListCtrl::OnNMRClick)
END_MESSAGE_MAP()


void CMyListCtrl::ClearImgVector(void)
{
	if (m_bkdImage)
	{
		delete m_bkdImage;
		m_bkdImage = NULL;
	}

	while (!m_vpImages.empty())
	{
		CBKDImage *bkdi = m_vpImages.back();
		m_vpImages.pop_back();

		if (bkdi)
		{
			delete bkdi;
		}
	}

	m_strCurrPath = _T("/"); //нужно для открытия нового образа
}

void CMyListCtrl::PushCurrentImg(void)
{
	m_vpImages.push_back(m_bkdImage);
}

BOOL CMyListCtrl::PopCurrentImg(void)
{
	if (m_vpImages.empty())
	{
		return FALSE;
	}

	if (m_bkdImage)
	{
		delete m_bkdImage;
	}

	m_bkdImage = m_vpImages.back();
	m_vpImages.pop_back();
	return TRUE;
}

void CMyListCtrl::PreSubclassWindow()
{
	LOGFONT lf;
	::GetObject((HGDIOBJ)::SendMessage(GetSafeHwnd(), WM_GETFONT, 0, 0), sizeof(LOGFONT), &lf);
	lf.lfWeight = FW_BOLD;
	m_fontBold.CreateFontIndirect(&lf);
	CListCtrl::PreSubclassWindow();
}

void CMyListCtrl::OnDestroy()
{
	ClearAllRecord();
	CListCtrl::OnDestroy();
}

void CMyListCtrl::OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMLVCUSTOMDRAW *pLVCustomDraw = reinterpret_cast<NMLVCUSTOMDRAW *>(pNMHDR);
	NMCUSTOMDRAW pLV = pLVCustomDraw->nmcd;
	// pLV.dwItemSpec - номер item'а
	*pResult = CDRF_DODEFAULT;

	switch (pLVCustomDraw->nmcd.dwDrawStage)
	{
		case CDDS_PREPAINT:
			*pResult |= CDRF_NOTIFYITEMDRAW;
			break;

		case CDDS_ITEMPREPAINT:
			FileRecord *fr = reinterpret_cast<FileRecord *>(GetItemData(pLV.dwItemSpec));

			if (fr->nAttr & (FR_ATTR_DIR | FR_ATTR_LOGDISK))
			{
				SelectObject(pLVCustomDraw->nmcd.hdc, m_fontBold);
			}

			if (fr->nAttr & FR_ATTR_PROTECTED)
			{
				pLVCustomDraw->clrText = RGB(0, 0, 160);
			}

			if (fr->nAttr & FR_ATTR_HIDDEN)
			{
				pLVCustomDraw->clrText = RGB(0x66, 0x66, 0x66);
			}

			if (fr->nAttr & FR_ATTR_LOGDISK)
			{
				//логический диск выделим своим цветом, даже если он protected or hidden
				pLVCustomDraw->clrText = RGB(0, 160, 0);
			}

			if (fr->nAttr & FR_ATTR_DELETED)
			{
				pLVCustomDraw->clrText = RGB(0xcc, 0xcc, 0xcc);
			}

			if (fr->nAttr & FR_ATTR_BAD)
			{
				pLVCustomDraw->clrText = RGB(0xcc, 0, 0);
			}

			break;
	}
}

void CMyListCtrl::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	int item = pNMListView->iItem;
	FileRecord *fr = reinterpret_cast<FileRecord *>(GetItemData(item));

	if ((pNMListView->uChanged & LVIF_STATE) && (pNMListView->uNewState & LVNI_SELECTED))
	{
		//реализация немного глюковата, но и так сойдёт
		BOOL bStatus = TRUE;
		BOOL bViewStatus = FALSE;

		if (!(fr->nAttr & (FR_ATTR_DIR | FR_ATTR_LOGDISK)))
		{
			bViewStatus = TRUE;
		}

		this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_EXTR, (WPARAM)bStatus, 0);
		this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_VIEW, (WPARAM)bViewStatus, 0);
	}

	*pResult = 0;
}

void CMyListCtrl::OnNMDblclk(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	ItemProcessing(pNMItemActivate->iItem);
	*pResult = 0;
}

void CMyListCtrl::ItemProcessing(int nItem, BOOL bViewAsText)
{
	if (nItem < 0)
	{
		return;
	}

	FileRecord *fr = reinterpret_cast<FileRecord *>(GetItemData(nItem));
	m_PaneInfo.nCurItem = nItem;

	if (fr->nAttr & FR_ATTR_DIR)
	{
		if (fr->strName == _T(".."))
		{
			if (m_PaneInfo.nCurDir > 0)
			{
				//выходим из подкаталога
				StepUptoDir();
			}
			else
			{
				//.. на корневой директории вызывает закрытие текущего открытого образа
				this->GetParentOwner()->PostMessage(WM_OUT_OF_IMAGE);
				m_strCurrPath = _T("/");
			}
		}
		else
		{
			//заходим в директорию
			StepIntoDir(fr);
		}

		OutDir(m_PaneInfo.nCurItem, m_PaneInfo.nCurDir);
	}
	else if (fr->nAttr & FR_ATTR_LOGDISK)
	{
		this->GetParentOwner()->PostMessage(WM_PUT_INTO_LOG, (WPARAM)(m_bkdImage->GetBaseOffset() + fr->nStartBlock * 512), (LPARAM)fr->nSize);
		m_strCurrPath += fr->strName;
	}
	else
	{
		//кликнули на файле, передаём запись файла в просмотрщик.
		if (bViewAsText)
		{
			if (!m_bkdImage->ViewFile(fr)) //проверка, файл это или нет, делается внутри.
			{
				this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
			}
		}
		else
		{
			if (!m_bkdImage->ViewFileAsSprite(fr)) //проверка, файл это или нет, делается внутри.
			{
				this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
			}
		}
	}
}

void CMyListCtrl::StepIntoDir(FileRecord *fr)
{
	CString t = fr->strName;
	t.Replace(_T('/'), _T('_')); //заменим слеш на другой символ, т.к. слеш у нас - разделитель подкаталогов

	if (m_strCurrPath == _T("/"))
	{
		m_strCurrPath += t;
	}
	else
	{
		m_strCurrPath += _T("/") + t;
	}

	OutCurrFilePath();
	m_vSelItems.push_back(m_PaneInfo); //сохраняем текущее
	//заполняем новое.
	m_PaneInfo.nCurDir = fr->nDirNum;
	m_PaneInfo.nParentDir = fr->nDirBelong;
	m_PaneInfo.nCurItem = 0;
}

void CMyListCtrl::StepUptoDir(void)
{
	int n = m_strCurrPath.ReverseFind(_T('/'));

	if (n == 0)
	{
		m_strCurrPath = _T("/");
	}
	else
	{
		m_strCurrPath = m_strCurrPath.Left(n);
	}

	OutCurrFilePath();
	m_PaneInfo = m_vSelItems.back();
	m_vSelItems.pop_back();
}

void CMyListCtrl::OutCurrFilePath(void)
{
	this->GetParentOwner()->SendMessage(WM_OUT_CURR_FILE_PATH, 0, reinterpret_cast<LPARAM>(&m_strCurrPath));
}

void CMyListCtrl::OutDiskInfo(void)
{
	this->GetParentOwner()->SendMessage(WM_OUT_CURR_IMG_INFO, 0, reinterpret_cast<LPARAM>(&m_bkdImage->GetInfo()));
}

void CMyListCtrl::ExtractSelected(const CString &strOutPath)
{
	size_t uSelectedCount = GetSelectedCount();
	int  nItem = -1;
	FileRecord *fr;

	if (uSelectedCount > 0)
	{
		//если что-то выделенное есть
		m_bkdImage->SetStorePath(strOutPath);

		for (size_t i = 0; i < uSelectedCount; ++i)
		{
			nItem = GetNextItem(nItem, LVNI_SELECTED);
			fr = reinterpret_cast<FileRecord *>(GetItemData(nItem));

			if (fr->strName == _T("..") && (fr->nAttr & FR_ATTR_DIR))
			{
				//если встретился выход в родительскую директорию, то
				//его проигнорируем.
			}
			else
			{
				if (!m_bkdImage->ExtractObject(fr, m_bCheckBinExtractStatus, m_bCheckLogExtractStatus)) //тут либо директория. либо файл. директорию надо рекурсивно извлечь целиком
				{
					this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
				}
			}
		}
	}
}

FILESAVE_STATUS CMyListCtrl::AddFile(CString &str)
{
	if (m_bkdImage)
	{
		FILESAVE_STATUS fs = m_bkdImage->AddFile(str, m_PaneInfo.nCurDir, m_bCheckBinExtractStatus);

		if (fs == FS_FILE_OPENERR)
		{
			this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
		}

		if (!m_bkdImage->ReadCatalog())
		{
			this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
		}

		RefreshDir();
		return fs;
	}
	else
	{
		return FS_NO_IMAGE;
	}
}

void CMyListCtrl::ViewSelected(BOOL bViewAsText)
{
	size_t uSelectedCount = GetSelectedCount();
	int  nItem = -1;
	FileRecord *fr;

	if (uSelectedCount > 0)
	{
		//если что-то выделенное есть
		for (size_t i = 0; i < uSelectedCount; ++i)
		{
			nItem = GetNextItem(nItem, LVNI_SELECTED);
			fr = reinterpret_cast<FileRecord *>(GetItemData(nItem));

			if (bViewAsText)
			{
				if (!m_bkdImage->ViewFile(fr)) //проверка, файл это или нет, делается внутри.
				{
					this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
				}
			}
			else
			{
				if (!m_bkdImage->ViewFileAsSprite(fr)) //проверка, файл это или нет, делается внутри.
				{
					this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
				}
			}
		}
	}
}

BOOL CMyListCtrl::SetImgName(const PARSE_RESULT &image, BOOL bLogDisk)
{
	ClearAllRecord();

	//по идее, теперь в функцию попадаем только если это истина.
	//поэтому оно всегда истинно.
	if (image.imageOSType != IMAGE_TYPE_ERROR && image.imageOSType != IMAGE_TYPE_UNKNOWN)
	{
		//теперь в зависимости от типа создаём нужный объект
		m_bkdImage = NULL;

		switch (image.imageOSType)
		{
			case IMAGE_TYPE_ANDOS:
				m_bkdImage = new CBKDAndos(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)TRUE, 0);
				break;

			case IMAGE_TYPE_DXDOS:
				// тоже использует андосный класс. (он теперь универсальный,
				// понимает любой формат фат12 за исключением правильных каталогов)
				// как только понадобится, на его основе можно будет сделать настоящий понимальщик мсдосного формата
				m_bkdImage = new CBKDAndos(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)TRUE, 0);
				break;

			case IMAGE_TYPE_MSDOS:
				m_bkdImage = new CBKDAndos(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)FALSE, 0);   //для мсдос пока не будем добавление разрешать
				break;

			case IMAGE_TYPE_MKDOS:
				m_bkdImage = new CBKDMkdos(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)TRUE, 0);
				break;

			case IMAGE_TYPE_AODOS:
				m_bkdImage = new CBKDAodos(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)TRUE, 0);
				break;

			case IMAGE_TYPE_NORD:
				m_bkdImage = new CBKDNord(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)TRUE, 0);
				break;

			case IMAGE_TYPE_MIKRODOS:
				m_bkdImage = new CBKDMikrodos(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)FALSE, 0);
				break;

			case IMAGE_TYPE_CSIDOS3:
				m_bkdImage = new CBKDCsidos3(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)TRUE, 0);
				break;

			case IMAGE_TYPE_RT11:
				m_bkdImage = new CBKDRT11(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)FALSE, 0);
				break;

			case IMAGE_TYPE_NCDOS:
				m_bkdImage = new CBKDNCdos(image);
				this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)FALSE, 0);
				break;

			default:
				ASSERT(FALSE);
		}

		if (m_bkdImage == NULL)
		{
			return FALSE;
		}

		SetSpecificColumn();

		//читаем каталог
		if (!m_bkdImage->ReadCatalog())
		{
			this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
		}

		//а потом, выводим корневой каталог.
		if (bLogDisk)
		{
			m_vSelItems.push_back(m_PaneInfo);
			m_PaneInfo.clear();
		}
		else
		{
			m_PaneInfo.clear();
			m_vSelItems.clear();
			m_vSelItems.push_back(m_PaneInfo);
		}

		OutCurrFilePath();
		RefreshDir();
		return TRUE;
	}

	return FALSE;
}

BOOL CMyListCtrl::ResetImgName(const PARSE_RESULT &image)
{
	//это при выходе из логического диска используется, восстанавливается предыдущее состояние
	ClearAllRecord();

	//объект у нас создан. надо только вернуть статус кнопки добавить
	switch (image.imageOSType)
	{
		case IMAGE_TYPE_ANDOS:
		case IMAGE_TYPE_DXDOS:
		case IMAGE_TYPE_MKDOS:
		case IMAGE_TYPE_AODOS:
		case IMAGE_TYPE_NORD:
		case IMAGE_TYPE_CSIDOS3:
			this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)TRUE, 0);
			break;

		default:
			this->GetParentOwner()->SendMessage(WM_ENABLE_BUTTON_ADD, (WPARAM)FALSE, 0);
	}

	SetSpecificColumn();

	if (!m_bkdImage->Reset())
	{
		this->GetParentOwner()->SendMessage(WM_SEND_ERRORNUM, (WPARAM)m_bkdImage->GetErrorNumber());
	}

	//каталог уже прочитан ранее
	//восстановим старое положение курсора.
	m_PaneInfo = m_vSelItems.back();
	m_vSelItems.pop_back();
	OutCurrFilePath();
	RefreshDir();
	return TRUE;
}

void CMyListCtrl::CloseImage(void)
{
	if (m_bkdImage)
	{
		delete m_bkdImage;
		m_bkdImage = NULL;
		DelSpecificColumn();
	}

	ClearAllRecord();
	m_PaneInfo.clear();
	m_vSelItems.clear();
	m_vSelItems.push_back(m_PaneInfo);
	m_strCurrPath = _T("/"); //корень диска.
	OutCurrFilePath();
}

void CMyListCtrl::ClearAllRecord(void)
{
	int items = GetItemCount();
	FileRecord *fr;
	int i = 0;

	//сперва поудаляем память, которую навыделяли
	while (i < items)
	{
		fr = reinterpret_cast<FileRecord *>(GetItemData(i++));

		if (fr)
		{
			delete fr;
		}
	}

	//а потом и все итемы из списка
	DeleteAllItems();
}

void CMyListCtrl::OutRecord(FileRecord *fr, int item)
{
	CString str;
	InsertItem(item, fr->strName);  //сюда можно добавить иконку

	if (fr->nAttr & FR_ATTR_DIR)
	{
		if (fr->nAddress)
		{
			fr->nAttr |= FR_ATTR_LINK; //добавляем, что это к тому же не просто директория, но ссылка, в некоторых ОС
			fr->nRecType = RECORD_TYPE_LINK;
		}

		//теперь ввыведем тип записи
		switch (fr->nRecType)
		{
			case RECORD_TYPE_UP:
				str = _T("UP");
				break;

			case RECORD_TYPE_DIR:
				str = _T("DIR");
				break;

			case RECORD_TYPE_LINK:
				str = _T("LINK");
				break;

			default:
				str = _T("???");
				break;
		}

		SetItemText(item, LC_TYPE_POS, str);
		str = _T("");
		SetItemText(item, LC_BLK_SIZE_POS, str);
		SetItemText(item, LC_ADDRESS_POS, str);
		SetItemText(item, LC_SIZE_POS, str);
	}
	else
	{
		//теперь ввыведем тип записи
		switch (fr->nRecType)
		{
			case RECORD_TYPE_LOGDSK:
				str = _T("LOG");
				break;

			case RECORD_TYPE_FILE:
				str = _T("");
				break;

			default:
				str = _T("???");
				break;
		}

		SetItemText(item, LC_TYPE_POS, str);
		str.Format(_T("%d"), fr->nBlkSize);
		SetItemText(item, LC_BLK_SIZE_POS, str);
		str.Format(_T("%o"), fr->nAddress);
		SetItemText(item, LC_ADDRESS_POS, str);
		str.Format(_T("%o"), fr->nSize);
		SetItemText(item, LC_SIZE_POS, str);
	}

	//выводим атрибуты
	str = _T("");

	if (fr->nAttr & FR_ATTR_DELETED)
	{
		str += _T("x");
	}

	if (fr->nAttr & FR_ATTR_BAD)
	{
		str += _T("B");
	}

	if (fr->nAttr & FR_ATTR_DIR)
	{
		str += _T("D");
	}

	if (fr->nAttr & FR_ATTR_LOGDISK)
	{
		str += _T("L");
	}

	if (fr->nAttr & FR_ATTR_PROTECTED)
	{
		str += _T("P");
	}

	if (fr->nAttr & FR_ATTR_HIDDEN)
	{
		str += _T("H");
	}

	if (fr->nAttr & FR_ATTR_TEMPORARY)
	{
		str += _T("T");
	}

	SetItemText(item, LC_ATTR_POS, str);

	if (m_bSpecificColumn)
	{
		if (fr->nAttr & FR_ATTR_DIR && fr->strName == _T(".."))
		{
			str = _T("");
		}
		else
		{
			str = m_bkdImage->GetSpecificData(fr->nSpecificData);
		}

		SetItemText(item, LC_SPECIFIC_POS, str);
	}

	FileRecord *nfr = new FileRecord(fr);
	//просто делать ссылку на fr нельзя, т.к. сюда передаётся адрес временной переменной.
	SetItemData(item, reinterpret_cast<DWORD_PTR>(nfr));
}

void CMyListCtrl::OutDir(int iCurItem, int iCurDir)
{
	int item = 0;
	FileRecord cur_fr;
	ClearAllRecord();
	BOOL bEnd = m_bkdImage->GetStartFileName(&cur_fr, iCurDir);

	if (bEnd)
	{
		OutRecord(&cur_fr, item++);
	}

	while (bEnd) //тут условие выхода
	{
		bEnd = m_bkdImage->GetNextFileName(&cur_fr, iCurDir);

		if (bEnd)
		{
			OutRecord(&cur_fr, item++);
		}
	}

	//сортируем, сперва директории, потом файлы, и ещё их по именам
	SortItems(MyCompareProc, reinterpret_cast<DWORD_PTR>(this));
	SetFocus();
	SetItemState(iCurItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
	SetHotItem(iCurItem);
}

void CMyListCtrl::RefreshDir()
{
	OutDir(m_PaneInfo.nCurItem, m_PaneInfo.nCurDir);
	OutDiskInfo();
}

int CALLBACK CMyListCtrl::MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CMyListCtrl *pList = reinterpret_cast<CMyListCtrl *>(lParamSort);
	LV_FINDINFO FindInfo;
	ZeroMemory(&FindInfo, sizeof(LVFINDINFO));
	FindInfo.flags = LVFI_PARAM;
	FindInfo.lParam = lParam1;
	int idx1 = pList->FindItem(&FindInfo);
	FindInfo.lParam = lParam2;
	int idx2 = pList->FindItem(&FindInfo);
	FileRecord *fr1 = reinterpret_cast<FileRecord *>(pList->GetItemData(idx1));
	FileRecord *fr2 = reinterpret_cast<FileRecord *>(pList->GetItemData(idx2));
	//сперва сортируем по типу записи, по возрастанию
	int res = fr1->nRecType - fr2->nRecType;

	if (res == 0)
	{
		//если равны - сортируем по возрастанию по именам
		res = fr1->strName.Compare(fr2->strName);
		return res;
	}
	else
	{
		return res;
	}

	//выход:
	// <0 если первый итем должен идти первым
	// >0 если второй итем должен идти первым
	// =0 если они равны
}

void CMyListCtrl::Init()
{
	CString strLCHdr;
	SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
	strLCHdr.LoadString(IDS_LC_FNAME_HDR);
	InsertColumn(LC_FNAME_POS, strLCHdr, LVCFMT_LEFT, 200);
	strLCHdr.LoadString(IDS_LC_TYPE_HDR);
	InsertColumn(LC_TYPE_POS, strLCHdr, LVCFMT_LEFT, 50);
	strLCHdr.LoadString(IDS_LC_BLKSZ_HDR);
	InsertColumn(LC_BLK_SIZE_POS, strLCHdr, LVCFMT_RIGHT, 34);
	strLCHdr.LoadString(IDS_LC_ADDRESS_HDR);
	InsertColumn(LC_ADDRESS_POS, strLCHdr, LVCFMT_RIGHT, 60);
	strLCHdr.LoadString(IDS_LC_SIZE_HDR);
	InsertColumn(LC_SIZE_POS, strLCHdr, LVCFMT_RIGHT, 70);
	strLCHdr.LoadString(IDS_LC_ATTR_HDR);
	InsertColumn(LC_ATTR_POS, strLCHdr, LVCFMT_LEFT, 70);
	UpdateData(FALSE);
}

void CMyListCtrl::SetSpecificColumn()
{
	UINT nID = m_bkdImage->HasSpecificData();

	if (nID)
	{
		AddSpecificColumn(nID);
	}
	else
	{
		DelSpecificColumn();
	}
}

void CMyListCtrl::AddSpecificColumn(UINT nID)
{
	CString strLCHdr;
	strLCHdr.LoadString(nID);

	if (m_bSpecificColumn) //если уже есть, то надо сменить заголовок
	{
		DeleteColumn(LC_SPECIFIC_POS); //проще удалить и заново создать, чем через структуру менять заголовок
	}

	InsertColumn(LC_SPECIFIC_POS, strLCHdr, LVCFMT_LEFT, 108);
	UpdateData(FALSE);
	m_bSpecificColumn = TRUE;
}

void CMyListCtrl::DelSpecificColumn()
{
	if (m_bSpecificColumn) //удаляем только если есть.
	{
		DeleteColumn(LC_SPECIFIC_POS);
		UpdateData(FALSE);
		m_bSpecificColumn = FALSE;
	}
}

UINT CMyListCtrl::GetImgFormatName()
{
	switch (m_bkdImage->GetImgOSType())
	{
		case IMAGE_TYPE_ERROR:
			return (IDS_IMGTYPE_ERROR);

		case IMAGE_TYPE_ANDOS:
			return (IDS_IMGTYPE_ANDOS);

		case IMAGE_TYPE_MKDOS:
			return (IDS_IMGTYPE_MKDOS);

		case IMAGE_TYPE_AODOS:
			return (IDS_IMGTYPE_AODOS);

		case IMAGE_TYPE_NORD:
			return (IDS_IMGTYPE_NORD);

		case IMAGE_TYPE_MIKRODOS:
			return (IDS_IMGTYPE_MICRODOS);

		case IMAGE_TYPE_CSIDOS3:
			return (IDS_IMGTYPE_CSIDOS3);

		case IMAGE_TYPE_RT11:
			return (IDS_IMGTYPE_RT11);

		case IMAGE_TYPE_NCDOS:
			return (IDS_IMGTYPE_NCDOS);

		case IMAGE_TYPE_DXDOS:
			return (IDS_IMGTYPE_DXDOS);

		case IMAGE_TYPE_MSDOS:
			return (IDS_IMGTYPE_MSDOS);

		default:
			return (IDS_IMGTYPE_UNKNOWN);
	}
}

void CMyListCtrl::OnDropFiles(HDROP hDropInfo)
{
	//по определённым причинам, сам список файлы дропать не умеет, поэтому попросит
	//хозяина, дропнуть ему файлов.
	this->GetParentOwner()->SendMessage(WM_MAKE_DROP, 0, reinterpret_cast<LPARAM>(hDropInfo));
}

static BOOL bSelectAll = FALSE;
static int nLastMouseClicked = 0;

void CMyListCtrl::ClearSelectAll()
{
	int items = GetItemCount();
	FileRecord *fr = NULL;

	for (int i = 0; i < items; ++i)
	{
		fr = reinterpret_cast<FileRecord *>(GetItemData(i));

		if (fr)
		{
			fr->bSelected = FALSE;
		}
	}

	SetItemState(-1, ~LVIS_SELECTED, LVIS_SELECTED);
	bSelectAll = FALSE;
}

void CMyListCtrl::SetSelectAll()
{
	int items = GetItemCount();
	FileRecord *fr = NULL;

	for (int i = 0; i < items; ++i)
	{
		fr = reinterpret_cast<FileRecord *>(GetItemData(i));

		if (fr)
		{
			fr->bSelected = TRUE;
		}
	}

	SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
	bSelectAll = TRUE;
}

void CMyListCtrl::SetSelectCurrent(int nItem)
{
	FileRecord *fr_current = reinterpret_cast<FileRecord *>(GetItemData(nItem));

	if (fr_current)
	{
		if (fr_current->bSelected)
		{
			SetItemState(nItem, ~LVIS_SELECTED, LVIS_SELECTED);
			fr_current->bSelected = FALSE;
		}
		else
		{
			SetItemState(nItem, LVIS_SELECTED, LVIS_SELECTED);
			fr_current->bSelected = TRUE;
		}
	}
}

void CMyListCtrl::SetSelectRange(int nFrom, int nTo)
{
	if (nFrom > nTo)
	{
		int t = nTo;
		nTo = nFrom;
		nFrom = t;
	}

	FileRecord *fr = NULL;

	for (int i = nFrom; i <= nTo; ++i)
	{
		fr = reinterpret_cast<FileRecord *>(GetItemData(i));

		if (fr)
		{
			fr->bSelected = TRUE;
		}
	}
}

void CMyListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	int nItem = -1;
	int nNextItem = -1;
	FileRecord *fr_current;
	//находим итем, на котором стоит курсор
	nItem = GetNextItem(nItem, LVIS_FOCUSED);

	if (nItem == -1) //если нету никаких итемов, то ничего не делаем
	{
		goto l_default;
	}

	fr_current = reinterpret_cast<FileRecord *>(GetItemData(nItem));

	switch (nChar)
	{
		case VK_RETURN:
			ItemProcessing(nItem);
			break;

		case 0x41:      // CTRL+A (Select all rows)
		{
			if (GetKeyState(VK_CONTROL) < 0)
			{
				if (!(GetStyle() & LVS_SINGLESEL))
				{
					SetSelectAll();
				}
			}

			break;
		}

		case VK_PRIOR:
		{
			if ((nFlags & KF_EXTENDED) && (GetKeyState(VK_CONTROL) < 0))
			{
				ItemProcessing(0);
				break;
			}
			else
			{
				goto l_default;
			}
		}

		case VK_NEXT:
		{
			if (
			    ((nFlags & KF_EXTENDED) && (GetKeyState(VK_CONTROL) < 0)) &&
			    (fr_current->nAttr & FR_ATTR_DIR)
			)
			{
				ItemProcessing(nItem);
				break;
			}
			else
			{
				goto l_default;
			}
		}

		case VK_INSERT:
		{
			//а не знаю, как по Ins выделить элемент, тут ведь движение стрелками снимает выделение. надо по-другому
			//как-то это дело обрабатывать, чтоб как в винраре было.
			nNextItem = GetNextItem(nItem, LVNI_BELOW);
			SetSelectCurrent(nItem);

			if (nNextItem >= 0)
			{
				SetItemState(nNextItem, LVIS_FOCUSED, LVIS_FOCUSED);

				if (!EnsureVisible(nNextItem, FALSE))
				{
					CRect r;
					GetItemRect(nNextItem, &r, LVIR_BOUNDS);
					Scroll(CSize(0, r.Height()));
				}
			}

			break;
		}

		case VK_UP:
		{
			if (bSelectAll)
			{
				ClearSelectAll();
			}

			nNextItem = GetNextItem(nItem, LVNI_ABOVE);

			if (nNextItem >= 0)
			{
				if (fr_current->bSelected)
				{
					SetItemState(nItem, ~(LVIS_FOCUSED), LVIS_FOCUSED);
				}
				else
				{
					SetItemState(nItem, ~(LVIS_FOCUSED | LVIS_SELECTED), LVIS_FOCUSED | LVIS_SELECTED);
				}

				SetItemState(nNextItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);

				if (!EnsureVisible(nNextItem, FALSE))
				{
					CRect r;
					GetItemRect(nNextItem, &r, LVIR_BOUNDS);
					Scroll(CSize(0, -r.Height()));
				}
			}

			break;
		}

		case VK_DOWN:
		{
			if (bSelectAll)
			{
				ClearSelectAll();
			}

			nNextItem = GetNextItem(nItem, LVNI_BELOW);

			if (nNextItem >= 0) //если дальше можно двигатся
			{
				if (fr_current->bSelected) //если запись, с которой уходим, выделена,
				{
					SetItemState(nItem, ~(LVIS_FOCUSED), LVIS_FOCUSED);    //то просто снимаем фокус
				}
				else
				{
					SetItemState(nItem, ~(LVIS_FOCUSED | LVIS_SELECTED), LVIS_FOCUSED | LVIS_SELECTED);    //иначе снимаем ещё и выделение
				}

				SetItemState(nNextItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);

				if (!EnsureVisible(nNextItem, FALSE))
				{
					CRect r;
					GetItemRect(nNextItem, &r, LVIR_BOUNDS);
					Scroll(CSize(0, r.Height()));
				}
			}

			break;
		}

		default:
l_default:
			CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
	}
}

void CMyListCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: добавьте свой код обработчика уведомлений
	*pResult = 0;

	if (GetKeyState(VK_SHIFT) < 0)
	{
		SetSelectRange(nLastMouseClicked, pNMItemActivate->iItem);
	}
	else if (GetKeyState(VK_CONTROL) < 0)
	{
		SetSelectCurrent(pNMItemActivate->iItem);
	}
	else
	{
		ClearSelectAll();
	}

	SetItemState(pNMItemActivate->iItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
	nLastMouseClicked = pNMItemActivate->iItem;
}

void CMyListCtrl::OnNMRClick(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: добавьте свой код обработчика уведомлений
	*pResult = 0;

	if (GetKeyState(VK_SHIFT) < 0)
	{
		SetSelectRange(nLastMouseClicked, pNMItemActivate->iItem);
	}
	else if (GetKeyState(VK_CONTROL) < 0)
	{
		SetSelectCurrent(pNMItemActivate->iItem);
	}
	else
	{
		ClearSelectAll();
	}

	SetItemState(pNMItemActivate->iItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
	nLastMouseClicked = pNMItemActivate->iItem;
}

