/*******************************************************************************
  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.38 2009/08/29 08:13:09 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/fileconf.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);
int g_nSortingColumn;

//******************************************************************************
// 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"), g_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_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", myListCtrl);
	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->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));
	// wxGTKでは直接wxLC_VIRTUALを指定しないと反映されない。
	this->list_ctrl->SetSingleStyle(wxLC_VIRTUAL);
	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, 150);
	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);
	g_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)
{
	// 処理対象のファイルを選択。
	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());

	// 各種設定。
	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"));	
	fdDlg.GetFilenames(mkDlg.files);
	mkDlg.InitDialog();
	mkDlg.cbDir->SetValue(swInfo.fnDestinationDirectory.GetFullPath());
	// 書庫名はファイル名の拡張子より前、もしくはディレクトリ名。
	mkDlg.cbFileName->SetValue(mkDlg.files.GetCount() == 1 ? wxFileName::FileName(mkDlg.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();
	swInfo.fSolid               = mkDlg.cbSolid->IsChecked();
	swInfo.fMMOptimize          = mkDlg.cbMMOptimize->IsChecked();
	swInfo.fEncryptHeader       = mkDlg.cbEncryptHeader->IsChecked();
	swInfo.sCompressLevel       = mkDlg.scLevel->GetValue();
	swInfo.sRecoveryRecord      = mkDlg.scRR->GetValue();
	swInfo.szPassword           = mkDlg.tcPassword->GetValue();
	swInfo.szKeyFile            = mkDlg.tcKeyfile->GetValue();
	swInfo.szComment            = mkDlg.tcComment->GetValue();
	{
		wxULongLong_t ll;
		mkDlg.cbSplitSize->GetValue().ToULongLong(& ll);
		swInfo.llSplitSize      = ll;
	}

	// TPIを読み込み。
	int nSelected = mkDlg.chType->GetSelection();
	wxFileName fnArchive = wxFileName::DirName(mkDlg.cbDir->GetValue());
	fnArchive.SetName(mkDlg.cbFileName->GetValue());
	fnArchive.SetExt(swInfo.fMakeSFX ? (wxString) EXE_EXT : mkDlg.defext[nSelected]);
	if (! tpi.InitLibrary(mkDlg.libname[nSelected], fnArchive.GetFullPath(), mkDlg.nLibNum[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(), mkDlg.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();
	this->list_ctrl->showFileInfo.Clear();

	// ツールバー・メニューバー設定。
	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);

	// 各種設定。
	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));
	fnArchive.SetExt(EXE_EXT);
	wxFileDialog fdDlg(this, wxT("Save as SFX"), fnArchive.GetPath(wxPATH_GET_VOLUME), fnArchive.GetFullName(), 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, false);
	mkDlg.InitDialog();
	if (mkDlg.ShowModal() == wxID_CANCEL)
	{
		return;
	}

	// 各種設定。
	TPI_SWITCHES swInfo;
	swInfo.fStoreDirectoryPathes = ! mkDlg.cbIgnorePath->IsChecked();
	swInfo.fnDestinationDirectory = wxFileName::DirName(mkDlg.cbDir->GetValue());
	swInfo.szPassword           = mkDlg.tcPassword->GetValue();
	swInfo.szKeyFile            = mkDlg.tcKeyfile->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, false);

	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, false);
	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, false);
	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 &)
{
	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->showFileInfo.Clear();
	g_hIconLL.RemoveAll();
	g_hIconLS.RemoveAll();

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

	// 配列と比較し、パスが一致しなければ消す。
	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() || ! TreeView_CheckNewerItem(this->tree_ctrl, this->tree_ctrl->GetLastChild(this->tree_ctrl->GetRootItem()), this->fileinfo[i].fnFileName.GetFullPath(), false))
			{
				continue;
			}

			this->list_ctrl->showFileInfo.Add(this->fileinfo[i]);
		}
	}
	// ソートして表示。
	this->list_ctrl->showFileInfo.Sort(& ListCtrlCompareProc);
	this->list_ctrl->SetItemCount(this->list_ctrl->showFileInfo.Count());
}

// ListView

void MainFrame::OnListItemDClick(wxListEvent&)
{
	// 展開対象を決定。
	wxArrayString files = MakeTargetFileList(this, true);

	// コマンドを取得。
	wxFileType * ftFile = wxTheMimeTypesManager->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;
	}

	// 各種設定。
	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;
	}

	// 展開対象を決定。
	wxFileDataObject objFile;
	wxArrayString files = MakeTargetFileList(this, false);
	for (size_t i = 0; i < files.Count(); i++)
	{
		// リストに追加。
		objFile.AddFile(swInfo.fnDestinationDirectory.GetPathWithSep() + wxFileName::FileName(files[i]).GetFullName());
	}

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

	wxDropSource dropSource(objFile, this);
	if (dropSource.DoDragDrop() != wxDragMove)
	{
#ifdef __LINUX__
		// Linuxではまだ処理が終わっていない(コンテキストメニューが表示されている)ので、とりあえず3秒だけ待つ。
		sleep(3);
#endif
		// 一時ディレクトリを削除。
		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;
	}

	// 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);
	wxTreeItemId idRoot = this->tree_ctrl->GetRootItem();
#ifdef __WINDOWS__
	wxTreeItemId idArchive = this->tree_ctrl->InsertItem(idRoot, idRoot, fn.GetFullName(), g_hIconT.Add(GetFileTypeIcon(fn)), -1);
#else
	wxTreeItemId idArchive = this->tree_ctrl->InsertItem(idRoot, idRoot, fn.GetFullName(), g_hIconT.Add(GetFileTypeIcon(fn).ConvertToImage().Rescale(16, 16)), -1);
#endif

	// ルートディレクトリを作成。
	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), true);

			// ディレクトリ属性を含むものについては除去。
			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);
	this->tree_ctrl->SelectItem(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);
	this->statusbar->SetStatusText(wxString::Format(wxT("%d file(s)"), this->fileinfo.Count()), 1);
	this->statusbar->SetStatusText(wxT("Unpacked: ") + aiInfo.llUnpackedSize.ToString() + wxT(" B"), 2);
	this->statusbar->SetStatusText(wxT("Packed: ")   + aiInfo.llPackedSize.ToString()   + wxT(" B"), 3);
	this->statusbar->SetStatusText(wxString::Format(wxT("Ratio: %3.1f%%"), aiInfo.wCompressRatio / 10.0), 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;
}
