/*******************************************************************************
  TPI - flexible but useless plug-in framework.
  Copyright (C) 2002-2009 Silky

  This library is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the Free
  Software Foundation; either version 2.1 of the License, or (at your option)
  any later version.

  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 Lesser General Public License
  for more details.

  You should have received a copy of the GNU Lesser General Public License along
  with this library; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

  $Id: frm_main.cpp,v 1.20 2009/08/12 12:26:49 sirakaba Exp $
*******************************************************************************/

#include "frontend-wx.h"

#include "frm_main.h"
#include "cls_filedroptarget.h"
#include "dlg_make.h"
#include "dlg_process.h"
#include "functions.h"

#include <wx/imaglist.h>
#include <wx/fileconf.h>
#include <wx/dynarray.h>
#include <wx/arrstr.h>
#include <wx/arrimpl.cpp>

WX_DEFINE_OBJARRAY(ArrayTPI_FILEINFO);

#define SetMenuToolState(id, state) this->toolbar->EnableTool(XRCID(id), state); this->menubar->Enable(XRCID(id), state)

//******************************************************************************
//    グローバル変数
//******************************************************************************

wxImageList g_hIconT(16, 16), g_hIconLL(32, 32), g_hIconLS(16, 16);

//******************************************************************************
// MainFrame
//******************************************************************************

MainFrame::MainFrame(): wxFrame()
{
}

MainFrame::~MainFrame()
{
	wxCommandEvent e;
	this->OnArcClose(e);

	// 設定を記録。
	wxFileConfig fc(wxEmptyString, wxEmptyString, ::wxGetCwd() + wxT("/frontend.conf"), wxEmptyString, wxCONFIG_USE_LOCAL_FILE);
	if (! this->IsIconized() && ! this->IsMaximized())
	{
		int a, b;
		this->GetSize(& a, & b);
		fc.Write(wxT("Window-Width"), a);
		fc.Write(wxT("Window-Height"), b);
		this->GetPosition(& a, & b);
		fc.Write(wxT("Window-X"), a);
		fc.Write(wxT("Window-Y"), b);
	}
	fc.Write(wxT("Splitter-Pos"), this->window_splitter->GetSashPosition());
	fc.Write(wxT("ListView-IconMode"), this->menubar->IsChecked(XRCID("Exe_View_Icons")));
	fc.Write(wxT("ListView-SortingColumn"), this->nSortingColumn);
	fc.Write(wxT("LastOpenPath"), this->fnLastOpenPath.GetFullPath());

	this->Close(true);
}

//******************************************************************************
// Event Table.
//******************************************************************************

BEGIN_EVENT_TABLE(MainFrame, wxFrame)
	EVT_INIT_DIALOG(      MainFrame::OnInit)
	// Menu
	EVT_MENU(XRCID("Arc_Create"),  MainFrame::OnArcCreate)
	EVT_MENU(XRCID("Arc_Open"),    MainFrame::OnArcOpen)
	EVT_MENU(XRCID("Arc_Close"),   MainFrame::OnArcClose)
	EVT_MENU(XRCID("Arc_Add"),     MainFrame::OnArcAdd)
	EVT_MENU(XRCID("Arc_SFX"),     MainFrame::OnArcSFX)
	EVT_MENU(XRCID("Arc_UnSFX"),   MainFrame::OnArcUnSFX)
	EVT_MENU(XRCID("Exe_Exit"),    MainFrame::OnExit)
	EVT_MENU(XRCID("Arc_Extract"), MainFrame::OnArcExtract)
	EVT_MENU(XRCID("Arc_Delete"),  MainFrame::OnArcDelete)
	EVT_MENU(XRCID("Arc_Test"),    MainFrame::OnArcTest)
	EVT_MENU(XRCID("Arc_Repair"),  MainFrame::OnArcRepair)
	EVT_MENU(XRCID("Exe_View_Icons"),  MainFrame::OnViewMode)
	EVT_MENU(XRCID("Exe_View_Details"),MainFrame::OnViewMode)
	EVT_MENU(XRCID("Exe_View_SelectAll"),MainFrame::OnSelectAll)
	// TreeView
	EVT_TREE_SEL_CHANGED(XRCID("TreeView"), MainFrame::OnTreeChanged)
	// ListView
	EVT_LIST_COL_CLICK(XRCID("ListView"), MainFrame::OnListColClick)
	EVT_LIST_ITEM_ACTIVATED(XRCID("ListView"), MainFrame::OnListItemDClick)
	EVT_LIST_BEGIN_DRAG(XRCID("ListView"), MainFrame::OnListBeginDrag)
END_EVENT_TABLE()

//******************************************************************************
// Event handler.
//******************************************************************************

void MainFrame::OnInit(wxInitDialogEvent&)
{
	// XRCと結びつけ。
	this->menubar    = this->GetMenuBar();
	this->toolbar    = this->GetToolBar();
	this->statusbar = XRCCTRL(* this, "statusbar", wxStatusBar);
	this->tree_ctrl = XRCCTRL(* this, "TreeView", wxTreeCtrl);
	this->list_ctrl = XRCCTRL(* this, "ListView", wxListCtrl);
	this->window_splitter = XRCCTRL(* this, "window_splitter", wxSplitterWindow);

	// 設定を読み込み。
	wxFileConfig fc(wxEmptyString, wxEmptyString, ::wxGetCwd() + wxT("/frontend.conf"), wxEmptyString, wxCONFIG_USE_LOCAL_FILE);
	this->SetSize(fc.Read(wxT("Window-X"), 0l), fc.Read(wxT("Window-Y"), 0l), fc.Read(wxT("Window-Width"), 800), fc.Read(wxT("Window-Height"), 400));
	this->nDetailShowFileNumLimit = fc.Read(wxT("DetailShowFileNumLimit"), 1000);
	this->nProgressShowFileNumLimit = fc.Read(wxT("ProgressShowFileNumLimit"), 1000);
	this->fnLastOpenPath = wxFileName::DirName(fc.Read(wxT("LastOpenPath"), ::wxGetCwd()));

	// 初期値設定。
	{
		wxIcon icon;
		icon.CopyFromBitmap(wxBitmap(FE_DIR_S_ICO wxT("app.png"), wxBITMAP_TYPE_ANY));
		this->SetIcon(icon);
	}

	wxCommandEvent e;
	e.SetId(fc.Read(wxT("ListView-IconMode"), 0l) ? XRCID("Exe_View_Icons") : XRCID("Exe_View_Details"));
	this->OnArcClose(e);
	this->OnViewMode(e);
	this->window_splitter->SetSashPosition(fc.Read(wxT("Splitter-Pos"), 200));
	this->list_ctrl->InsertColumn(0, wxT("Filename"),      wxLIST_FORMAT_LEFT,  140);
	this->list_ctrl->InsertColumn(1, wxT("Unpacked"),      wxLIST_FORMAT_RIGHT,  80);
	this->list_ctrl->InsertColumn(2, wxT("Packed"),        wxLIST_FORMAT_RIGHT,  80);
	this->list_ctrl->InsertColumn(3, wxT("Ratio"),         wxLIST_FORMAT_RIGHT,  50);
	this->list_ctrl->InsertColumn(4, wxT("Method"),        wxLIST_FORMAT_LEFT,   60);
	this->list_ctrl->InsertColumn(5, wxT("Attr"),          wxLIST_FORMAT_LEFT,   50);
	this->list_ctrl->InsertColumn(6, wxT("Last modified"), wxLIST_FORMAT_RIGHT, 140);
	this->list_ctrl->InsertColumn(7, wxT("Path"),          wxLIST_FORMAT_LEFT,  100);
	this->list_ctrl->InsertColumn(8, wxT("Type"),          wxLIST_FORMAT_LEFT,  100);
	this->list_ctrl->InsertColumn(9, wxT("No."),           wxLIST_FORMAT_LEFT,   35);
	this->nSortingColumn = fc.Read(wxT("ListView-SortingColumn"), 9);

	this->list_ctrl->SetDropTarget(new myFileDropTarget(this));

	int nStatusBarParts[] = {40, 60, 120, 120, 90, 1000};
	this->statusbar->SetFieldsCount(6, nStatusBarParts);

	// コマンドライン読み込み。
	this->cmdLine.SetSwitchChars(wxT("-"));
	this->cmdLine.AddSwitch(wxT("x"), wxEmptyString, wxT("(command) Extract filenames in archive."));
	this->cmdLine.AddSwitch(wxT("l"), wxEmptyString, wxT("(command) List archive(default)."));
	this->cmdLine.AddSwitch(wxT("r"), wxEmptyString, wxT("Use stored pathes(recommend)."));
	this->cmdLine.AddParam(wxT("archive"),   wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
	this->cmdLine.AddParam(wxT("filenames"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE);
	if (this->cmdLine.Parse() == 0)
	{
		if (this->cmdLine.GetParamCount() == 0)
		{
			return;
		}

		if (this->cmdLine.Found(wxT("x")))
		{
			// 書庫を展開。
		}
		else
		{
			// 書庫を開く。
			wxFileName fnArchive(this->cmdLine.GetParam(0));
			fnArchive.Normalize(wxPATH_NORM_ALL, this->szCurrentPath);
			this->LoadArc(fnArchive.GetFullPath());
		}
	}
}

// MenuBar

void MainFrame::OnExit(wxCommandEvent&)
{
	this->Close(true);
}

void MainFrame::OnArcCreate(wxCommandEvent& e)
{
	// 処理対象のファイルを選択。
	wxArrayString files;
	{
		wxFileDialog fdDlg(this, wxT("Choose files to compress"), this->fnLastOpenPath.GetFullPath(), wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
		if (fdDlg.ShowModal() == wxID_CANCEL)
		{
			return;
		}
		// ここでwxFileName::FileNameを用いているのは、後でディレクトリ名を必要とする場合があるため。
		// あまりよい仕様ではないが動くのでとりあえず放置。
		this->fnLastOpenPath = wxFileName::FileName(fdDlg.GetDirectory());
		fdDlg.GetFilenames(files);
	}

	for (size_t i = 0 ; i < files.GetCount(); i++)
	{
		files[i].Replace(wxT("\\"), wxT("/"));
	}

	// 各種設定。
	TPI_SWITCHES swInfo;
	swInfo.fnDestinationDirectory = wxFileName::DirName(this->fnLastOpenPath.GetFullPath());
	swInfo.pCustomSwitches = NULL;
	this->statusbar->SetStatusText(swInfo.fnDestinationDirectory.GetFullPath(), 5);

	// 作成ダイアログを設定。
	MakeDialog mkDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_make.xrc"));
	::wxXmlResource::Get()->LoadDialog(& mkDlg, this, wxT("dlg_make"));	
	mkDlg.InitDialog();
	mkDlg.cbDir->SetValue(swInfo.fnDestinationDirectory.GetFullPath());
	// 書庫名はファイル名の拡張子より前、もしくはディレクトリ名。
	mkDlg.cbFileName->SetValue(files.GetCount() == 1 ? wxFileName::FileName(files[0]).GetName() : this->fnLastOpenPath.GetFullName());

	// ダイアログを表示。
	if (mkDlg.ShowModal() == wxID_CANCEL)
	{
		this->statusbar->SetStatusText(wxEmptyString, 5);
		return;
	}
	this->statusbar->SetStatusText(wxEmptyString, 5);

	// 各種設定。
	swInfo.fStoreDirectoryPathes = ! mkDlg.cbIgnorePath->IsChecked();
	swInfo.fMakeSFX = mkDlg.cbMakeSFX->IsChecked();

	// TPIを読み込み。
	int nSelected = mkDlg.chType->GetSelection();
	wxFileName fnArchive = wxFileName::DirName(mkDlg.cbDir->GetValue());
	fnArchive.SetName(mkDlg.cbFileName->GetValue());
	fnArchive.SetExt(swInfo.fMakeSFX ? wxT("exe") : mkDlg.defext[nSelected]);
	if (! tpi.InitLibrary(mkDlg.libname[nSelected], fnArchive.GetFullPath(), nSelected))
	{
		::wxMessageBox(wxT("Fault: InitLibrary()!"));
		return;
	}

	// 処理を行う。
	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));
	procDlg.InitDialog();
	procDlg.Show(true);
	int nErrorCode = this->ErrorCheck(tpi.Command(TPI_COMMAND_ADD, & swInfo, fnArchive.GetFullPath(), files));
	procDlg.Show(false);
	tpi.FreeLibrary();
	if (nErrorCode != TPI_ERROR_SUCCESS)
	{
		return;
	}

	if (mkDlg.cbOpenAfter->IsChecked())
	{
		// 作成先を開く。
#ifdef __WINDOWS__
		::wxExecute(wxT("explorer ") + swInfo.fnDestinationDirectory.GetFullPath());
#endif
	}

	if (mkDlg.cbExitAfter->IsChecked())
	{
		// 終了。
		this->Close(true);
	}

	// 終了しない場合は書庫を開く。
	this->OnArcClose(e);
	this->LoadArc(fnArchive.GetFullPath());
}

void MainFrame::OnArcOpen(wxCommandEvent& e)
{
	// 書庫を選択。
	wxString szFileName;
	{
		wxFileDialog fdDlg(this);
		fdDlg.SetDirectory(this->fnLastOpenPath.GetFullPath());
		if (fdDlg.ShowModal() == wxID_CANCEL)
		{
			return;
		}
		this->fnLastOpenPath = wxFileName::DirName(fdDlg.GetDirectory());
		szFileName = fdDlg.GetPath();
	}

	// 書庫を読み込み。
	this->OnArcClose(e);
	this->LoadArc(szFileName);
}

void MainFrame::OnArcClose(wxCommandEvent&)
{
	// ツリービュー・リストビュー設定。
	this->tree_ctrl->DeleteAllItems();
	this->list_ctrl->DeleteAllItems();

	// ツールバー・メニューバー設定。
	SetMenuToolState("Arc_Close",   false);
	SetMenuToolState("Arc_Add",     false);
	SetMenuToolState("Arc_SFX",     false);
	SetMenuToolState("Arc_UnSFX",   false);
	SetMenuToolState("Arc_Extract", false);
	SetMenuToolState("Arc_Delete",  false);
	SetMenuToolState("Arc_Test",    false);
	SetMenuToolState("Arc_Repair",  false);

	for (int i = 0; i < this->statusbar->GetFieldsCount(); i++)
	{
		this->statusbar->SetStatusText(wxEmptyString, i);
	}
	this->fileinfo.Clear();
	g_hIconT.RemoveAll();
	g_hIconLL.RemoveAll();
	g_hIconLS.RemoveAll();
	this->tpi.FreeLibrary();
}

void MainFrame::OnArcAdd(wxCommandEvent& e)
{
	// 処理対象のファイルを選択。
	wxArrayString files;
	wxFileDialog fdDlg(this, wxT("Choose files to add"), wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
	if (fdDlg.ShowModal() == wxID_CANCEL)
	{
		return;
	}
	fdDlg.GetFilenames(files);
	for (size_t i = 0 ; i < files.GetCount(); i++)
	{
		files[i].Replace(wxT("\\"), wxT("/"));
	}

	// 各種設定。
	TPI_SWITCHES swInfo;
	swInfo.fnDestinationDirectory = wxFileName::DirName(fdDlg.GetDirectory());
	swInfo.fMakeSFX = false;
	swInfo.pCustomSwitches = NULL;
	wxString szArcName = this->statusbar->GetStatusText(5);

	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	procDlg.Show(true);
	this->ErrorCheck(this->tpi.Command(TPI_COMMAND_ADD, & swInfo, szArcName, files));
	procDlg.Show(false);

	// 書庫を再読み込み。
	this->OnArcClose(e);
	this->LoadArc(szArcName);
}

void MainFrame::OnArcSFX(wxCommandEvent&)
{
	// 保存先を尋ねる。
	wxFileName fnArchive(this->statusbar->GetStatusText(5));
	wxFileDialog fdDlg(this, wxT("Save as SFX"), fnArchive.GetPath(wxPATH_GET_VOLUME), fnArchive.GetName() + wxT(".exe"), wxFileSelectorDefaultWildcardStr, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
	if (fdDlg.ShowModal() == wxID_CANCEL)
	{
		return;
	}
	this->fnLastOpenPath = wxFileName::DirName(fdDlg.GetDirectory());

	wxArrayString files;
	files.Add(fdDlg.GetPath());

	TPI_SWITCHES swInfo;
	swInfo.fMakeSFX = true;
	swInfo.fnDestinationDirectory = wxFileName::DirName(fdDlg.GetDirectory());

	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	procDlg.Show(true);
	this->ErrorCheck(this->tpi.Command(TPI_COMMAND_SFX, & swInfo, fnArchive.GetFullPath(), files));
	procDlg.Show(false);
}

void MainFrame::OnArcUnSFX(wxCommandEvent&)
{
	// 保存先を尋ねる。
	wxFileName fnArchive(this->statusbar->GetStatusText(5));
	wxFileDialog fdDlg(this, wxT("Save as normal archive"), fnArchive.GetPath(wxPATH_GET_VOLUME), fnArchive.GetName(), wxFileSelectorDefaultWildcardStr, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
	if (fdDlg.ShowModal() == wxID_CANCEL)
	{
		return;
	}
	this->fnLastOpenPath = wxFileName::DirName(fdDlg.GetDirectory());

	wxArrayString files;
	files.Add(fdDlg.GetPath());

	TPI_SWITCHES swInfo;
	swInfo.fMakeSFX = true;
	swInfo.fnDestinationDirectory = wxFileName::DirName(fdDlg.GetDirectory());

	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	procDlg.Show(true);
	this->ErrorCheck(this->tpi.Command(TPI_COMMAND_UNSFX, & swInfo, fnArchive.GetFullPath(), files));
	procDlg.Show(false);
}

void MainFrame::OnArcExtract(wxCommandEvent&)
{
	// 展開ダイアログを作成。
	MakeDialog mkDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_make.xrc"));
	::wxXmlResource::Get()->LoadDialog(& mkDlg, this, wxT("dlg_make"));
	mkDlg.bIsMake = false;
	mkDlg.files   = MakeTargetFileList(this);
	mkDlg.InitDialog();
	if (mkDlg.ShowModal() == wxID_CANCEL)
	{
		return;
	}

	// 各種設定。
	TPI_SWITCHES swInfo;
	swInfo.fStoreDirectoryPathes = ! mkDlg.cbIgnorePath->IsChecked();
	swInfo.fnDestinationDirectory = wxFileName::DirName(mkDlg.cbDir->GetValue());
	swInfo.pCustomSwitches = NULL;

	// 必要なら書庫名でディレクトリを作成する。
	if (WillMakeDirByArcName(this, & mkDlg))
	{
		swInfo.fnDestinationDirectory = MakeDirPath(swInfo.fnDestinationDirectory, wxFileName::FileName(this->statusbar->GetStatusText(5)).GetFullName(), true);
		if (! swInfo.fnDestinationDirectory.IsOk())
		{
			::wxMessageBox(wxT("Fault: Unable to make the destination directory!"));
			return;
		}
	}

	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	if (mkDlg.files.Count() < this->nProgressShowFileNumLimit)
	{
		procDlg.Show(true);
	}
	this->ErrorCheck(this->tpi.Command(TPI_COMMAND_EXTRACT, & swInfo, this->statusbar->GetStatusText(5), mkDlg.files));
	procDlg.Show(false);

	if (mkDlg.cbOpenAfter->IsChecked())
	{
		// 展開先を開く。
#ifdef __WINDOWS__
		::wxExecute(wxT("explorer ") + swInfo.fnDestinationDirectory.GetFullPath());
#endif
	}

	if (mkDlg.cbExitAfter->IsChecked())
	{
		// 終了。
		this->Close(true);
	}
}

void MainFrame::OnArcDelete(wxCommandEvent& e)
{
	// 全ファイル削除は危険ではないかと。
	if (this->list_ctrl->GetSelectedItemCount() == 0)
	{
		return;
	}

	if (::wxMessageBox(wxT("Are you sure to delete selected files?"), wxMessageBoxCaptionStr, wxYES_NO | wxICON_EXCLAMATION, this) == wxNO)
	{
		return;
	}

	// 各種設定。
	TPI_SWITCHES swInfo;
	wxString szArcName = this->statusbar->GetStatusText(5);
	wxArrayString files = MakeTargetFileList(this);

	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	procDlg.Show(true);
	this->ErrorCheck(this->tpi.Command(TPI_COMMAND_DELETE, & swInfo, szArcName, files));
	procDlg.Show(false);	

	// 書庫を再読み込みする。
	this->OnArcClose(e);
	this->LoadArc(szArcName);
}

void MainFrame::OnArcTest(wxCommandEvent&)
{
	// 各種設定。
	wxArrayString files = MakeTargetFileList(this);
	TPI_SWITCHES swInfo;

	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	procDlg.Show(true);
	if (this->ErrorCheck(this->tpi.Command(TPI_COMMAND_TEST, & swInfo, this->statusbar->GetStatusText(5), files)) == TPI_ERROR_SUCCESS)
	{
		::wxMessageBox(wxT("This is a correct archive."), wxT("Frontend"), wxOK | wxCENTRE | wxICON_INFORMATION, this);
	}
	procDlg.Show(false);
}

void MainFrame::OnArcRepair(wxCommandEvent&)
{
	// 各種設定。
	wxArrayString files = MakeTargetFileList(this);
	TPI_SWITCHES swInfo;

	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	procDlg.Show(true);
	this->ErrorCheck(this->tpi.Command(TPI_COMMAND_REPAIR, & swInfo, this->statusbar->GetStatusText(5), files));
	procDlg.Show(false);	
}

void MainFrame::OnViewMode(wxCommandEvent & e)
{
	int n = e.GetId();
	this->menubar->Check(n, true);
	this->list_ctrl->SetSingleStyle(wxLC_REPORT, n == XRCID("Exe_View_Details"));
	this->list_ctrl->SetSingleStyle(wxLC_ICON,   n == XRCID("Exe_View_Icons"));
}

void MainFrame::OnSelectAll(wxCommandEvent & WXUNUSED(event))
{
	for (int i = 0; i < this->list_ctrl->GetItemCount(); i++)
	{
		this->list_ctrl->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
	}
}

// TreeView

void MainFrame::OnTreeChanged(wxTreeEvent& e)
{
	// ツリービューからパスを取得。
	wxString szNodePath = TreeView_GetItemPath(this->tree_ctrl, e.GetItem());
	// リストビューを消去。
	this->list_ctrl->DeleteAllItems();

	// 全表示で大量のファイルがある場合は表示するか尋ねる。
	if (szNodePath == wxT("*") && this->fileinfo.Count() > this->nDetailShowFileNumLimit
		&& ::wxMessageBox(wxT("This archive stores many files, so it take long to show file list. Really do you want to look at these file list?"), wxMessageBoxCaptionStr, wxYES_NO | wxICON_EXCLAMATION, this) == wxNO)
	{
		return;
	}

	// アイコン設定。
	this->list_ctrl->SetImageList(& g_hIconLL, wxIMAGE_LIST_NORMAL);
	this->list_ctrl->SetImageList(& g_hIconLS, wxIMAGE_LIST_SMALL);

	int nShow = 0;
	// 配列と比較し、パスが一致しなければ消す。
	for (size_t i = 0; i < this->fileinfo.GetCount(); i++)
	{
		// パスを比較。
		if (szNodePath == wxT("*") || szNodePath == this->fileinfo[i].fnFileName.GetPath())
		{
			// 項目がフォルダであるなら無視。
			if (this->fileinfo[i].fnFileName.IsDir())
			{
				continue;
			}

			// アイコンを取得。
			int nId = -1;
			wxMimeTypesManager mimeFile;
			wxIconLocation ilFile;
			wxFileType * ftFile = mimeFile.GetFileTypeFromExtension(this->fileinfo[i].fnFileName.GetExt());
			wxIcon icon;
			if (ftFile && ftFile->GetIcon(& ilFile))
			{
				if (! ::wxFileExists(ilFile.GetFileName()))
				{
					icon.CopyFromBitmap(wxBitmap((ilFile.GetFileName() == wxT("%1") || ilFile.GetFileName() == wxT("\"%1\"")) ? FE_DIR_S_ICO wxT("exe.png") : FE_DIR_S_ICO wxT("file.png"), wxBITMAP_TYPE_ANY));
				}
				else
				{
					wxIcon i(ilFile);
					icon = i;
				}
			}
			else
			{
				icon.CopyFromBitmap(wxBitmap(FE_DIR_S_ICO wxT("file.png"), wxBITMAP_TYPE_ANY));
			}
			nId = g_hIconLL.Add(icon);
			g_hIconLS.Add(icon);

			// リストビューに項目を追加。
			wxString szTemp;
			this->list_ctrl->InsertItem(nShow, this->fileinfo[i].fnFileName.GetFullName(), nId);
			this->list_ctrl->SetItem(nShow, 1, this->fileinfo[i].llUnpackedSize.ToString());
			this->list_ctrl->SetItem(nShow, 2, this->fileinfo[i].llPackedSize.ToString());
			szTemp.Printf(wxT("%3.1f%%"), this->fileinfo[i].wCompressRatio / 10.0);
			this->list_ctrl->SetItem(nShow, 3, szTemp);
			this->list_ctrl->SetItem(nShow, 4, this->fileinfo[i].szMethod);
			szTemp = wxT("---w-");
			szTemp[0] = this->fileinfo[i].dwAttribute & TPI_ATTRIBUTE_DIRECTORY ? wxT('d') : 
			            this->fileinfo[i].dwAttribute & TPI_ATTRIBUTE_ARCHIVE   ? wxT('a') : wxT('-');
			szTemp[1] = this->fileinfo[i].dwAttribute & TPI_ATTRIBUTE_SYSTEM    ? wxT('s') : wxT('-');
			szTemp[2] = this->fileinfo[i].dwAttribute & TPI_ATTRIBUTE_HIDDEN    ? wxT('h') : wxT('-');
			szTemp[3] = this->fileinfo[i].dwAttribute & TPI_ATTRIBUTE_READONLY  ? wxT('r') : wxT('w');
			szTemp[4] = this->fileinfo[i].dwAttribute & TPI_ATTRIBUTE_ENCRYPTED ? wxT('g') : wxT('-');
			this->list_ctrl->SetItem(nShow, 5, szTemp);
			this->list_ctrl->SetItem(nShow, 6, this->fileinfo[i].tmModified.Format(wxT("%Y/%m/%d %H:%M:%S")));
			this->list_ctrl->SetItem(nShow, 7, this->fileinfo[i].fnFileName.GetPath());
			szTemp = this->fileinfo[i].fnFileName.GetExt() + wxT(" file");
			if (ftFile)
			{
				ftFile->GetDescription(& szTemp);
			}
			this->list_ctrl->SetItem(nShow, 8, szTemp);

			szTemp.Printf(wxT("%d"), i);
			this->list_ctrl->SetItem(nShow, 9, szTemp);
			this->list_ctrl->SetItemPtrData(nShow++, (wxUIntPtr) & this->fileinfo[i]);
		}
	}

	// ソート。
	this->list_ctrl->SortItems(ListViewCompareProc, this->nSortingColumn);
}

// ListView

void MainFrame::OnListColClick(wxListEvent& e)
{
	this->nSortingColumn = e.GetColumn();
	this->list_ctrl->SortItems(ListViewCompareProc, this->nSortingColumn);
}

void MainFrame::OnListItemDClick(wxListEvent&)
{
	// 展開対象を決定。
	wxArrayString files;
	for (int i = 0; i < this->list_ctrl->GetItemCount(); i++)
	{
		// 単一ファイルのみ対応。
		if (this->list_ctrl->GetItemState(i, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED)
		{
			wxListItem item;
			item.SetId(i);
			item.SetColumn(9);
			item.SetMask(wxLIST_MASK_TEXT);
			this->list_ctrl->GetItem(item);
			long l = 0;
			item.GetText().ToLong(& l);
			files.Add(this->fileinfo[l].szStoredName);
			break;
		}
	}

	// コマンドを取得。
	wxMimeTypesManager mimeFile;
	wxFileType * ftFile = mimeFile.GetFileTypeFromExtension(wxFileName::FileName(files[0]).GetExt());
	if (! ftFile)
	{
		::wxMessageBox(wxT("Fault: Unable to get the file type!"));
		return;
	}

	// 各種設定。
	TPI_SWITCHES swInfo;
	swInfo.fStoreDirectoryPathes = false;
	swInfo.pCustomSwitches = NULL;
	swInfo.fnDestinationDirectory = MakeDirPath(wxFileName::DirName(::wxGetCwd()), wxT("tpi_tmp"), true);
	if (! swInfo.fnDestinationDirectory.IsOk())
	{
		::wxMessageBox(wxT("Fault: Unable to make the temporary directory!"));
		return;
	}

	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	procDlg.Show(true);
	int nErrorCode = this->ErrorCheck(this->tpi.Command(TPI_COMMAND_EXTRACT, & swInfo, this->statusbar->GetStatusText(5), files));
	procDlg.Show(false);

	// コマンドを実行。
	wxString s = swInfo.fnDestinationDirectory.GetPathWithSep() + wxFileName::FileName(files[0]).GetFullName();
	if (nErrorCode == TPI_ERROR_SUCCESS)
	{
		::wxExecute(ftFile->GetOpenCommand(s), wxEXEC_SYNC);
	}

	// 一時ディレクトリを削除。
	::wxRemoveFile(s);
	::wxRmdir(swInfo.fnDestinationDirectory.GetFullPath());
}

void MainFrame::OnListBeginDrag(wxListEvent&)
{
	if (this->list_ctrl->GetSelectedItemCount() == 0)
	{
		// アイテムを選択せずドラッグしようとした場合。
		return;
	}

	wxFileDataObject objFile;

	// 各種設定。
	TPI_SWITCHES swInfo;
	swInfo.fStoreDirectoryPathes = false;
	swInfo.pCustomSwitches = NULL;
	swInfo.fnDestinationDirectory = MakeDirPath(wxFileName::DirName(::wxGetCwd()), wxT("tpi_tmp"), true);
	if (! swInfo.fnDestinationDirectory.IsOk())
	{
		::wxMessageBox(wxT("Fault: Unable to make the temporary directory!"));
		return;
	}

	// 展開対象を決定。
	wxArrayString files;
	for (int i = 0; i < this->list_ctrl->GetItemCount() && files.GetCount() < (unsigned) this->list_ctrl->GetSelectedItemCount(); i++)
	{
		if (this->list_ctrl->GetItemState(i, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED)
		{
			wxListItem item;
			item.SetId(i);
			item.SetColumn(9);
			item.SetMask(wxLIST_MASK_TEXT);
			this->list_ctrl->GetItem(item);
			long l = 0;
			item.GetText().ToLong(& l);
			files.Add(this->fileinfo[l].szStoredName);

			// リストに追加。
			objFile.AddFile(swInfo.fnDestinationDirectory.GetPathWithSep() + this->fileinfo[l].fnFileName.GetFullName());
		}
	}

	this->ErrorCheck(tpi.Command(TPI_COMMAND_EXTRACT, & swInfo, this->statusbar->GetStatusText(5), files));

	wxDropSource dropSource;
	dropSource.SetData(objFile);
	if (dropSource.DoDragDrop() != wxDragMove)
	{
		// 一時ディレクトリを削除。
		wxArrayString szFileNames = objFile.GetFilenames();
		for (size_t i = 0; i < szFileNames.GetCount(); i++)
		{
			::wxRemoveFile(szFileNames[i]);
		}
		::wxRmdir(swInfo.fnDestinationDirectory.GetFullPath());
	}
}

// イベントハンドラ以外。

void MainFrame::LoadArc(wxString szFileName)
{
	if (szFileName.IsEmpty())
	{
		return;
	}
	szFileName.Replace(wxT("\\"), wxT("/"));

	// TPIを読み込み。
	wxFileSystem fs;
	fs.ChangePathTo(FE_DIR_B_LIB, true);
	wxString szTPIName = fs.FindFirst(wxT("*" TPI_EXT), wxFILE);
	int nFileCount = -1;
	while (! szTPIName.IsEmpty())
	{
		// 対応確認。
		if (! tpi.InitLibrary(szTPIName, szFileName, 0) || ! tpi.CheckArchive(szFileName, & nFileCount) || nFileCount < 0)
		{
			tpi.FreeLibrary();
			szTPIName = fs.FindNext();
			continue;
		}
		break;
	}
	if (nFileCount == -1)
	{
		tpi.FreeLibrary();
		::wxMessageBox(wxT("Fault : No plug-in supporting this archive was found!"));
		return;
	}

	// 進捗ダイアログ表示。ただし、ファイル数が多いときは速度を優先して表示しない。
	ProcessDialog procDlg;
	::wxXmlResource::Get()->Load(FE_DIR_S_XRC wxT("dlg_process.xrc"));
	::wxXmlResource::Get()->LoadDialog(& procDlg, this, wxT("dlg_process"));	
	procDlg.InitDialog();
	if ((unsigned) nFileCount < this->nProgressShowFileNumLimit)
	{
		procDlg.Show(true);
	}

	// 最初のコールバックを送信。
	TPI_PROCESSINFO piInfo;
	piInfo.uMessage = TPI_MESSAGE_STATUS;
	piInfo.uStatus = 0x1000;
	piInfo.fiInfo.fnFileName = wxFileName::FileName(szFileName);
	piInfo.fiInfo.llUnpackedSize = nFileCount;
	TPICallbackProc(TPI_NOTIFY_COMMON, & piInfo);

	// 書庫を開く。
	void * hArc = NULL;
	if (tpi.OpenArchive(szFileName, & hArc) != TPI_ERROR_SUCCESS)
	{
		procDlg.Show(false);
		tpi.FreeLibrary();
		::wxMessageBox(wxT("Fault : OpenArchive()!"));
		return;
	}

	// 画像リストを作成。
	this->tree_ctrl->SetImageList(& g_hIconT);
	this->tree_ctrl->AddRoot(wxEmptyString);

	// 書庫のアイコンを取得し、書庫ルートを作成。
	wxFileName fn(szFileName);
	wxMimeTypesManager mimeArchive;
	wxIconLocation ilArchive;
	wxIcon icon;
	wxFileType * ftArchive = mimeArchive.GetFileTypeFromExtension(fn.GetExt());
	if (ftArchive && ftArchive->GetIcon(& ilArchive))
	{
		if (ilArchive.GetFileName() == wxT("%1"))
		{
			icon.CopyFromBitmap(wxBitmap(FE_DIR_S_ICO wxT("exe.png"), wxBITMAP_TYPE_ANY));
		}
		else
		{
			wxIcon i(ilArchive);
			icon = i;
		}
	}
	else
	{
		icon.CopyFromBitmap(wxBitmap(FE_DIR_S_ICO wxT("archive.png"), wxBITMAP_TYPE_ANY));
	}
	wxTreeItemId idRoot = this->tree_ctrl->GetRootItem();
	wxTreeItemId idArchive = this->tree_ctrl->InsertItem(idRoot, idRoot, fn.GetFullName(), g_hIconT.Add(icon), -1);

	// ルートディレクトリを作成。
	wxTreeItemId idArcRoot = this->tree_ctrl->InsertItem(idRoot, idArchive, wxT("-----"), g_hIconT.Add(wxBitmap(FE_DIR_S_ICO wxT("Folder_closed.png"), wxBITMAP_TYPE_ANY)), -1);

	// 配列のサイズを確保。
	this->fileinfo.Alloc(nFileCount);

	// ファイル情報をロード。
	TPI_FILEINFO fiInfo;
	if (tpi.GetFileInformation(hArc, & fiInfo, true) == TPI_ERROR_SUCCESS)
	{
		do
		{
			piInfo.uStatus = 0x1001;
			piInfo.fiInfo.fnFileName = fiInfo.fnFileName;
			piInfo.llProcessedSize = this->fileinfo.GetCount();
			TPICallbackProc(TPI_NOTIFY_COMMON, & piInfo);

			// ツリービューに反映。
			TreeView_CheckNewerItem(this->tree_ctrl, idArcRoot, fiInfo.fnFileName.GetPath(wxPATH_GET_VOLUME, wxPATH_UNIX));

			// ディレクトリ属性を含むものについては除去。
			if (fiInfo.dwAttribute & TPI_ATTRIBUTE_DIRECTORY)
			{
				continue;
			}

			// 情報を保存してカウントアップ。
			this->fileinfo.Add(fiInfo);
			piInfo.uStatus = 0x1002;
			TPICallbackProc(TPI_NOTIFY_COMMON, & piInfo);
		}
		while (tpi.GetFileInformation(hArc, & fiInfo, false) == TPI_ERROR_SUCCESS);
	}
	this->fileinfo.Shrink();
	this->tree_ctrl->ExpandAllChildren(idArcRoot);
	this->tree_ctrl->ScrollTo(idArchive);

	// 書庫の情報を取得。
	TPI_ARCHIVEINFO aiInfo;
	if (tpi.GetArchiveInformation(hArc, & aiInfo) != TPI_ERROR_SUCCESS)
	{
		procDlg.Show(false);
		tpi.FreeLibrary();
		::wxMessageBox(wxT("Fault : GetArchiveInformation()!"));
		return;
	}

	wxString szStatusText;
	this->statusbar->SetStatusText(wxEmptyString, 0);
	szStatusText.Printf(wxT("%d file(s)"), nFileCount);
	this->statusbar->SetStatusText(szStatusText, 1);
	this->statusbar->SetStatusText(wxT("Unpacked: ") + aiInfo.llUnpackedSize.ToString() + wxT(" B"), 2);
	this->statusbar->SetStatusText(wxT("Packed: ")   + aiInfo.llPackedSize.ToString()   + wxT(" B"), 3);
	szStatusText.Printf(wxT("Ratio: %3.1f%%"), aiInfo.wCompressRatio / 10.0);
	this->statusbar->SetStatusText(szStatusText, 4);
	this->statusbar->SetStatusText(szFileName, 5);

	// 書庫を閉じる。
	if (tpi.CloseArchive(hArc) != TPI_ERROR_SUCCESS)
	{
		::wxMessageBox(wxT("Fault : CloseArchive()!"));
	}

	// コールバック関数を設定。
	if (tpi.SetCallbackProc(TPICallbackProc) != TPI_ERROR_SUCCESS)
	{
		::wxMessageBox(wxT("Fault : SetCallbackProc()!"));
	}

	// UI各種設定。
	{
		// ツールバー・メニューバー設定。
		SetMenuToolState("Arc_Close",   true);
		SetMenuToolState("Arc_Add",     true);
		SetMenuToolState("Arc_SFX",     aiInfo.nSFXType == 0);
		SetMenuToolState("Arc_UnSFX",   aiInfo.nSFXType != 0);
		SetMenuToolState("Arc_Extract", true);
		SetMenuToolState("Arc_Delete",  true);
		SetMenuToolState("Arc_Test",    true);
		SetMenuToolState("Arc_Repair",  true);
	}

	procDlg.Show(false);
}

int MainFrame::ErrorCheck(int nErrorCode)
{
	wxString s;
	switch (nErrorCode)
	{
	case TPI_ERROR_SUCCESS:
		return TPI_ERROR_SUCCESS;
	case TPI_ERROR_D_UNSUPPORTED:
		s.Printf(wxT("Sorry, this functions is not supported yet."), nErrorCode);
		break;
	case TPI_ERROR_D_SKIPPED:
		s.Printf(wxT("This operation is canceled by the user."), nErrorCode);
		break;
	default:
		s.Printf(wxT("ErrorCode:%d"), nErrorCode);
	}
	::wxMessageBox(s, wxT("Frontend"), wxOK | wxCENTRE | wxICON_ERROR, this);
	return nErrorCode;
}
