1022 lines
26 KiB
C++
1022 lines
26 KiB
C++
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU Affero General Public License as
|
||
|
// published by the Free Software Foundation, either version 3 of the
|
||
|
// License, or (at your option) any later version.
|
||
|
//
|
||
|
// This program 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 Affero General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU Affero General Public License
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "data_mirror.h"
|
||
|
#include "data_mirrorDlg.h"
|
||
|
#include "progress_dialog.h"
|
||
|
#include <sys/timeb.h>
|
||
|
#include "nel/misc/file.h"
|
||
|
|
||
|
using namespace std;
|
||
|
using namespace NLMISC;
|
||
|
|
||
|
#define PATH_WIDTH 500
|
||
|
#define SIZE_WIDTH 60
|
||
|
#define DATE_WIDTH 100
|
||
|
#define TYPE_WIDTH 80
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CAboutDlg dialog used for App About
|
||
|
|
||
|
#ifndef ListView_SetCheckState
|
||
|
#define ListView_SetCheckState(hwndLV, i, fCheck) \
|
||
|
ListView_SetItemState(hwndLV, i, \
|
||
|
INDEXTOSTATEIMAGEMASK((fCheck)+1), LVIS_STATEIMAGEMASK)
|
||
|
#endif
|
||
|
|
||
|
CString GetString (uint res)
|
||
|
{
|
||
|
CString str;
|
||
|
str.LoadString (res);
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
class CAboutDlg : public CDialog
|
||
|
{
|
||
|
public:
|
||
|
CAboutDlg();
|
||
|
|
||
|
// Dialog Data
|
||
|
//{{AFX_DATA(CAboutDlg)
|
||
|
enum { IDD = IDD_ABOUTBOX };
|
||
|
//}}AFX_DATA
|
||
|
|
||
|
// ClassWizard generated virtual function overrides
|
||
|
//{{AFX_VIRTUAL(CAboutDlg)
|
||
|
protected:
|
||
|
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
||
|
//}}AFX_VIRTUAL
|
||
|
|
||
|
// Implementation
|
||
|
protected:
|
||
|
//{{AFX_MSG(CAboutDlg)
|
||
|
//}}AFX_MSG
|
||
|
DECLARE_MESSAGE_MAP()
|
||
|
};
|
||
|
|
||
|
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
|
||
|
{
|
||
|
//{{AFX_DATA_INIT(CAboutDlg)
|
||
|
//}}AFX_DATA_INIT
|
||
|
}
|
||
|
|
||
|
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
|
||
|
{
|
||
|
CDialog::DoDataExchange(pDX);
|
||
|
//{{AFX_DATA_MAP(CAboutDlg)
|
||
|
//}}AFX_DATA_MAP
|
||
|
}
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
|
||
|
//{{AFX_MSG_MAP(CAboutDlg)
|
||
|
// No message handlers
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CData_mirrorDlg dialog
|
||
|
|
||
|
CData_mirrorDlg::CData_mirrorDlg(CWnd* pParent /*=NULL*/)
|
||
|
: CDialog(CData_mirrorDlg::IDD, pParent)
|
||
|
{
|
||
|
//{{AFX_DATA_INIT(CData_mirrorDlg)
|
||
|
ModifiedFilter = 0;
|
||
|
//}}AFX_DATA_INIT
|
||
|
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
|
||
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
||
|
ButtonXModifiedFromRight = 0;
|
||
|
SortOrder = true;
|
||
|
SortedColumn = 0;
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::DoDataExchange(CDataExchange* pDX)
|
||
|
{
|
||
|
CDialog::DoDataExchange(pDX);
|
||
|
//{{AFX_DATA_MAP(CData_mirrorDlg)
|
||
|
DDX_Control(pDX, IDIGNORE, IgnoreCtrl);
|
||
|
DDX_Control(pDX, IDC_MODIFIED_FILTERS, ModifiedFilterCtrl);
|
||
|
DDX_Control(pDX, IDC_ADDED_FILTERS, AddedFilterCtrl);
|
||
|
DDX_Control(pDX, IDC_REMOVED_FILTERS, RemovedFilterCtrl);
|
||
|
DDX_Control(pDX, IDC_LIST, List);
|
||
|
DDX_Radio(pDX, IDC_MODIFIED_FILTERS, ModifiedFilter);
|
||
|
//}}AFX_DATA_MAP
|
||
|
}
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CData_mirrorDlg, CDialog)
|
||
|
//{{AFX_MSG_MAP(CData_mirrorDlg)
|
||
|
ON_WM_SYSCOMMAND()
|
||
|
ON_WM_PAINT()
|
||
|
ON_WM_QUERYDRAGICON()
|
||
|
ON_BN_CLICKED(IDIGNORE, OnIgnore)
|
||
|
ON_WM_SIZE()
|
||
|
ON_NOTIFY(NM_CLICK, IDC_LIST, OnClickList)
|
||
|
ON_BN_CLICKED(IDUPDATE, OnUpdate)
|
||
|
ON_BN_CLICKED(IDC_ADDED_FILTERS, OnAddedFilters)
|
||
|
ON_BN_CLICKED(IDC_MODIFIED_FILTERS, OnModifiedFilters)
|
||
|
ON_BN_CLICKED(IDC_REMOVED_FILTERS, OnRemovedFilters)
|
||
|
ON_NOTIFY(NM_DBLCLK, IDC_LIST, OnClickList)
|
||
|
ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST, OnColumnclickList)
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CData_mirrorDlg message handlers
|
||
|
|
||
|
BOOL CData_mirrorDlg::OnInitDialog()
|
||
|
{
|
||
|
CDialog::OnInitDialog();
|
||
|
|
||
|
// Add "About..." menu item to system menu.
|
||
|
|
||
|
// IDM_ABOUTBOX must be in the system command range.
|
||
|
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
|
||
|
ASSERT(IDM_ABOUTBOX < 0xF000);
|
||
|
|
||
|
CMenu* pSysMenu = GetSystemMenu(FALSE);
|
||
|
if (pSysMenu != NULL)
|
||
|
{
|
||
|
CString strAboutMenu;
|
||
|
strAboutMenu.LoadString(IDS_ABOUTBOX);
|
||
|
if (!strAboutMenu.IsEmpty())
|
||
|
{
|
||
|
pSysMenu->AppendMenu(MF_SEPARATOR);
|
||
|
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Set the icon for this dialog. The framework does this automatically
|
||
|
// when the application's main window is not a dialog
|
||
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
||
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
||
|
|
||
|
// Init position remainder
|
||
|
RECT client;
|
||
|
RECT childClient;
|
||
|
GetClientRect (&client);
|
||
|
ModifiedFilterCtrl.GetWindowRect (&childClient);
|
||
|
ScreenToClient (&childClient);
|
||
|
ButtonXModifiedFromRight = client.right - childClient.left;
|
||
|
AddedFilterCtrl.GetWindowRect (&childClient);
|
||
|
ScreenToClient (&childClient);
|
||
|
ButtonXAddedFromRight = client.right - childClient.left;
|
||
|
RemovedFilterCtrl.GetWindowRect (&childClient);
|
||
|
ScreenToClient (&childClient);
|
||
|
ButtonXRemovedFromRight = client.right - childClient.left;
|
||
|
List.GetWindowRect (&childClient);
|
||
|
ScreenToClient (&childClient);
|
||
|
ListBottomFromBottom = client.bottom - childClient.bottom;
|
||
|
ListRightFromRight = client.right - childClient.right;
|
||
|
|
||
|
// Init list
|
||
|
ListView_SetExtendedListViewStyle (List, LVS_EX_CHECKBOXES);
|
||
|
SHFILEINFO sfi;
|
||
|
HIMAGELIST imageListSmall = (HIMAGELIST)SHGetFileInfo( TEXT("C:\\"), 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
|
||
|
CImageList *imageList = CImageList::FromHandle( imageListSmall );
|
||
|
List.SetImageList( imageList, LVSIL_SMALL);
|
||
|
|
||
|
resize ();
|
||
|
buildSourceFiles ();
|
||
|
updateList ();
|
||
|
updateSort ();
|
||
|
|
||
|
return TRUE; // return TRUE unless you set the focus to a control
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::updateList ()
|
||
|
{
|
||
|
UpdateData ();
|
||
|
|
||
|
// Checks
|
||
|
nlassert ((ModifiedFilter>=Modified) && (ModifiedFilter<=Removed));
|
||
|
|
||
|
// Delete item data
|
||
|
uint count = List.GetItemCount ();
|
||
|
uint i;
|
||
|
for (i=0; i<count; i++)
|
||
|
delete (std::list<CEntryFile>::iterator*)List.GetItemData(i);
|
||
|
|
||
|
List.DeleteAllItems ();
|
||
|
List.DeleteColumn (5);
|
||
|
List.DeleteColumn (4);
|
||
|
List.DeleteColumn (3);
|
||
|
List.DeleteColumn (2);
|
||
|
List.DeleteColumn (1);
|
||
|
List.DeleteColumn (0);
|
||
|
switch (ModifiedFilter)
|
||
|
{
|
||
|
case Modified:
|
||
|
List.InsertColumn (0, GetString (IDS_NAME));
|
||
|
List.InsertColumn (1, GetString (IDS_NEW_SIZE));
|
||
|
List.InsertColumn (2, GetString (IDS_OLD_SIZE));
|
||
|
List.InsertColumn (3, GetString (IDS_NEW_DATE));
|
||
|
List.InsertColumn (4, GetString (IDS_OLD_DATE));
|
||
|
List.InsertColumn (5, GetString (IDS_TYPE));
|
||
|
break;
|
||
|
case Added:
|
||
|
List.InsertColumn (0, GetString (IDS_NAME));
|
||
|
List.InsertColumn (1, GetString (IDS_NEW_SIZE));
|
||
|
List.InsertColumn (2, GetString (IDS_NEW_DATE));
|
||
|
List.InsertColumn (3, GetString (IDS_TYPE));
|
||
|
break;
|
||
|
case Removed:
|
||
|
List.InsertColumn (0, GetString (IDS_NAME));
|
||
|
List.InsertColumn (1, GetString (IDS_OLD_SIZE));
|
||
|
List.InsertColumn (2, GetString (IDS_OLD_DATE));
|
||
|
List.InsertColumn (3, GetString (IDS_TYPE));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Add the items
|
||
|
List.SetColumnWidth (0, PATH_WIDTH);
|
||
|
|
||
|
// Sub string
|
||
|
uint subString = 1;
|
||
|
|
||
|
// Add the sizes
|
||
|
if (ModifiedFilter != Added)
|
||
|
{
|
||
|
List.SetColumnWidth (subString++, SIZE_WIDTH);
|
||
|
}
|
||
|
if (ModifiedFilter != Removed)
|
||
|
{
|
||
|
List.SetColumnWidth (subString++, SIZE_WIDTH);
|
||
|
}
|
||
|
|
||
|
// Add the dates
|
||
|
if (ModifiedFilter != Added)
|
||
|
{
|
||
|
List.SetColumnWidth (subString++, DATE_WIDTH);
|
||
|
}
|
||
|
if (ModifiedFilter != Removed)
|
||
|
{
|
||
|
List.SetColumnWidth (subString++, DATE_WIDTH);
|
||
|
}
|
||
|
|
||
|
// Add the type
|
||
|
List.SetColumnWidth (subString++, TYPE_WIDTH);
|
||
|
|
||
|
// Next item
|
||
|
|
||
|
|
||
|
// Add items
|
||
|
std::list<CEntryFile> &entries = Files[ModifiedFilter];
|
||
|
std::list<CEntryFile>::iterator ite = entries.begin ();
|
||
|
while (ite != entries.end ())
|
||
|
{
|
||
|
// Add the items
|
||
|
const CEntryFile &entry = *ite;
|
||
|
uint nItem = List.InsertItem (0, entry.Strings[CEntryFile::Path].c_str (), entry.Image);
|
||
|
List.SetItemData (nItem, DWORD(new std::list<CEntryFile>::iterator (ite)));
|
||
|
|
||
|
// Sub string
|
||
|
subString = 1;
|
||
|
|
||
|
// Add the sizes
|
||
|
if (ModifiedFilter != Removed)
|
||
|
{
|
||
|
List.SetItemText (nItem, subString++, entry.Strings[CEntryFile::NewSize].c_str ());
|
||
|
}
|
||
|
if (ModifiedFilter != Added)
|
||
|
{
|
||
|
List.SetItemText (nItem, subString++, entry.Strings[CEntryFile::OldSize].c_str ());
|
||
|
}
|
||
|
|
||
|
// Add the dates
|
||
|
if (ModifiedFilter != Removed)
|
||
|
{
|
||
|
List.SetItemText (nItem, subString++, entry.Strings[CEntryFile::NewDate].c_str ());
|
||
|
}
|
||
|
if (ModifiedFilter != Added)
|
||
|
{
|
||
|
List.SetItemText (nItem, subString++, entry.Strings[CEntryFile::OldDate].c_str ());
|
||
|
}
|
||
|
|
||
|
// Add the type
|
||
|
List.SetItemText (nItem, subString++, entry.Strings[CEntryFile::Type].c_str ());
|
||
|
|
||
|
// Next item
|
||
|
ite++;
|
||
|
}
|
||
|
|
||
|
UpdateData (FALSE);
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnSysCommand(UINT nID, LPARAM lParam)
|
||
|
{
|
||
|
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
|
||
|
{
|
||
|
CAboutDlg dlgAbout;
|
||
|
dlgAbout.DoModal();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CDialog::OnSysCommand(nID, lParam);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If you add a minimize button to your dialog, you will need the code below
|
||
|
// to draw the icon. For MFC applications using the document/view model,
|
||
|
// this is automatically done for you by the framework.
|
||
|
|
||
|
void CData_mirrorDlg::OnPaint()
|
||
|
{
|
||
|
if (IsIconic())
|
||
|
{
|
||
|
CPaintDC dc(this); // device context for painting
|
||
|
|
||
|
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
|
||
|
|
||
|
// Center icon in client rectangle
|
||
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
||
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
||
|
CRect rect;
|
||
|
GetClientRect(&rect);
|
||
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
||
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
||
|
|
||
|
// Draw the icon
|
||
|
dc.DrawIcon(x, y, m_hIcon);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CDialog::OnPaint();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The system calls this to obtain the cursor to display while the user drags
|
||
|
// the minimized window.
|
||
|
HCURSOR CData_mirrorDlg::OnQueryDragIcon()
|
||
|
{
|
||
|
return (HCURSOR) m_hIcon;
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnIgnore()
|
||
|
{
|
||
|
UpdateData ();
|
||
|
|
||
|
// Update files
|
||
|
uint count = List.GetItemCount ();
|
||
|
uint i;
|
||
|
std::list<CEntryFile> &entries = Files[ModifiedFilter];
|
||
|
for (i=0; i<count; i++)
|
||
|
{
|
||
|
// Checked ?
|
||
|
if (ListView_GetCheckState (List, i))
|
||
|
{
|
||
|
// Get the file
|
||
|
CString itemText = List.GetItemText (i, 0);
|
||
|
|
||
|
// Add to ignore list
|
||
|
IgnoreFiles.insert ((const char*)itemText);
|
||
|
|
||
|
// Remove from the file list
|
||
|
std::list<CEntryFile>::iterator *ite = (std::list<CEntryFile>::iterator *)List.GetItemData (i);
|
||
|
nlassert (ite);
|
||
|
entries.erase (*ite);
|
||
|
delete ite;
|
||
|
|
||
|
// Remove the item
|
||
|
List.DeleteItem (i);
|
||
|
i--;
|
||
|
count--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void createDirectory (const string &dir)
|
||
|
{
|
||
|
string temp = dir;
|
||
|
temp = temp.substr (0, temp.size ()-1);
|
||
|
temp = NLMISC::CFile::getPath (temp);
|
||
|
if (!temp.empty ())
|
||
|
createDirectory (temp);
|
||
|
NLMISC::CFile::createDirectory (dir);
|
||
|
}
|
||
|
|
||
|
bool setFileTime (const char *filename, const FILETIME &result)
|
||
|
{
|
||
|
HANDLE handle = CreateFile (filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
if (handle)
|
||
|
{
|
||
|
SetFileTime (handle, NULL, NULL, &result);
|
||
|
CloseHandle (handle);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnOK()
|
||
|
{
|
||
|
// Update first
|
||
|
OnUpdate ();
|
||
|
|
||
|
bool success = true;
|
||
|
|
||
|
// Update window
|
||
|
CProgressDialog progress;
|
||
|
progress.Create (CProgressDialog::IDD, this);
|
||
|
progress.ShowWindow (SW_SHOW);
|
||
|
progress.progress (0);
|
||
|
|
||
|
uint totalCount = (uint)FilesToUpdate[Modified].size () + (uint)FilesToUpdate[Added].size () + (uint)FilesToUpdate[Removed].size ();
|
||
|
uint currentFile = 0;
|
||
|
|
||
|
// System time
|
||
|
SYSTEMTIME systemTime;
|
||
|
GetSystemTime(&systemTime);
|
||
|
FILETIME fileTime;
|
||
|
nlverify (SystemTimeToFileTime (&systemTime, &fileTime));
|
||
|
|
||
|
// Update files
|
||
|
std::vector<string> &modifiedList = FilesToUpdate[Modified];
|
||
|
uint count = (uint)modifiedList.size ();
|
||
|
uint i;
|
||
|
for (i=0; i<count; i++)
|
||
|
{
|
||
|
// Copy it
|
||
|
const string &itemText = modifiedList[i];
|
||
|
string source = MainDirectory+itemText;
|
||
|
string dest = MirrorDirectory+itemText;
|
||
|
|
||
|
progress.DisplayString = "Copy \"" + source + "\" to \"" + dest + "\"";
|
||
|
progress.progress (float(currentFile++)/(float(totalCount)));
|
||
|
|
||
|
string directory = NLMISC::CFile::getPath (dest);
|
||
|
createDirectory (directory.c_str ());
|
||
|
if (!CopyFile (source.c_str (), dest.c_str (), FALSE))
|
||
|
{
|
||
|
MessageBox (("Can't copy file "+source+" in file "+dest).c_str (), "NeL Data Mirror",
|
||
|
MB_OK|MB_ICONEXCLAMATION);
|
||
|
success = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!LogDirectory.empty())
|
||
|
{
|
||
|
string sTmp = LogDirectory + "data_mirror.txt";
|
||
|
FILE *f = fopen(sTmp.c_str(),"at");
|
||
|
fprintf(f,"Modified file : %s\n", dest.c_str());
|
||
|
fclose(f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Touch
|
||
|
setFileTime (dest.c_str(), fileTime);
|
||
|
}
|
||
|
|
||
|
std::vector<string> &addedList = FilesToUpdate[Added];
|
||
|
count = (uint)addedList.size ();
|
||
|
for (i=0; i<count; i++)
|
||
|
{
|
||
|
// Copy it
|
||
|
const string &itemText = addedList[i];
|
||
|
string source = MainDirectory+itemText;
|
||
|
string dest = MirrorDirectory+itemText;
|
||
|
string directory = NLMISC::CFile::getPath (dest);
|
||
|
|
||
|
progress.DisplayString = "Copy \"" + source + "\" to \"" + dest + "\"";
|
||
|
progress.progress (float(currentFile++)/(float(totalCount)));
|
||
|
|
||
|
createDirectory (directory.c_str ());
|
||
|
if (!CopyFile (source.c_str (), dest.c_str (), FALSE))
|
||
|
{
|
||
|
MessageBox (("Can't copy file "+source+" in file "+dest).c_str (), "NeL Data Mirror",
|
||
|
MB_OK|MB_ICONEXCLAMATION);
|
||
|
success = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!LogDirectory.empty())
|
||
|
{
|
||
|
string sTmp = LogDirectory + "data_mirror.txt";
|
||
|
FILE *f = fopen(sTmp.c_str(),"at");
|
||
|
fprintf(f,"Added file : %s\n", dest.c_str());
|
||
|
fclose(f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Touch
|
||
|
setFileTime (dest.c_str(), fileTime);
|
||
|
}
|
||
|
|
||
|
std::vector<string> &removedList = FilesToUpdate[Removed];
|
||
|
count = (uint)removedList.size ();
|
||
|
for (i=0; i<count; i++)
|
||
|
{
|
||
|
// Copy it
|
||
|
const string &itemText = removedList[i];
|
||
|
string dest = MirrorDirectory+itemText;
|
||
|
|
||
|
progress.DisplayString = "Delete \"" + dest + "\"";
|
||
|
progress.progress (float(currentFile++)/(float(totalCount)));
|
||
|
|
||
|
if (!DeleteFile (dest.c_str ()))
|
||
|
{
|
||
|
MessageBox (("Can't delete the file "+dest).c_str (), "NeL Data Mirror",
|
||
|
MB_OK|MB_ICONEXCLAMATION);
|
||
|
success = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!LogDirectory.empty())
|
||
|
{
|
||
|
string sTmp = LogDirectory + "data_mirror.txt";
|
||
|
FILE *f = fopen(sTmp.c_str(),"at");
|
||
|
fprintf(f,"Removed file : %s\n", dest.c_str());
|
||
|
fclose(f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FILE *file = fopen ((IgnoreDirectory+"ignore_list.txt").c_str (), "w");
|
||
|
if (file)
|
||
|
{
|
||
|
// Save ignore list
|
||
|
std::set<string>::iterator ite = IgnoreFiles.begin ();
|
||
|
while (ite != IgnoreFiles.end ())
|
||
|
{
|
||
|
fputs ((*ite + "\n").c_str (), file);
|
||
|
|
||
|
ite++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CDialog::OnOK();
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::resize ()
|
||
|
{
|
||
|
// Initialised ?
|
||
|
if (ButtonXModifiedFromRight)
|
||
|
{
|
||
|
RECT client;
|
||
|
RECT child;
|
||
|
GetClientRect (&client);
|
||
|
ModifiedFilterCtrl.GetClientRect (&child);
|
||
|
ModifiedFilterCtrl.SetWindowPos (NULL, client.right - ButtonXModifiedFromRight, child.top, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
||
|
AddedFilterCtrl.SetWindowPos (NULL, client.right - ButtonXAddedFromRight, child.top, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
||
|
RemovedFilterCtrl.SetWindowPos (NULL, client.right - ButtonXRemovedFromRight, child.top, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
||
|
List.SetWindowPos (NULL, 0, 0, client.right - ListRightFromRight, client.bottom - ListBottomFromBottom, SWP_NOMOVE|SWP_NOZORDER);
|
||
|
|
||
|
Invalidate ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnSize(UINT nType, int cx, int cy)
|
||
|
{
|
||
|
CDialog::OnSize(nType, cx, cy);
|
||
|
resize ();
|
||
|
}
|
||
|
|
||
|
bool getFileTime (const char *filename, FILETIME &result)
|
||
|
{
|
||
|
HANDLE handle = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
if (handle)
|
||
|
{
|
||
|
FILETIME res0;
|
||
|
FILETIME res1;
|
||
|
GetFileTime (handle, &res0, &res1, &result);
|
||
|
CloseHandle (handle);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::buildSourceFiles ()
|
||
|
{
|
||
|
UpdateData (FALSE);
|
||
|
|
||
|
Files[Modified].clear ();
|
||
|
Files[Added].clear ();
|
||
|
Files[Removed].clear ();
|
||
|
|
||
|
// List all files in database
|
||
|
vector<string> fileSource;
|
||
|
CPath::getPathContent (MainDirectory+CurrentDir, true, false, true, fileSource);
|
||
|
uint i;
|
||
|
uint count = (uint)fileSource.size ();
|
||
|
for (i=0; i<count; i++)
|
||
|
{
|
||
|
// Get the filename
|
||
|
string &str = fileSource[i];
|
||
|
str = toLower(str.substr (MainDirectory.size (), str.size ()));
|
||
|
|
||
|
// In the ignore list ?
|
||
|
if (IgnoreFiles.find (str) == IgnoreFiles.end () && (str != "ignore_list.txt"))
|
||
|
{
|
||
|
// Does the destination exist ?
|
||
|
string mirrorFile = MirrorDirectory+str;
|
||
|
string mainFile = MainDirectory+str;
|
||
|
if (NLMISC::CFile::fileExists (mirrorFile))
|
||
|
{
|
||
|
FILETIME time0;
|
||
|
FILETIME time1;
|
||
|
getFileTime (mirrorFile.c_str (), time0);
|
||
|
getFileTime (mainFile.c_str (), time1);
|
||
|
sint64 deltaInt = (((uint64)time1.dwHighDateTime)<<32|((uint64)time1.dwLowDateTime)) - (((uint64)time0.dwHighDateTime)<<32|((uint64)time0.dwLowDateTime));
|
||
|
double deltaInSec = (double)deltaInt;
|
||
|
deltaInSec /= 10000000.0;
|
||
|
deltaInSec = fabs(deltaInSec);
|
||
|
|
||
|
if (deltaInSec > 2.0)
|
||
|
{
|
||
|
if (BinaryCompare)
|
||
|
{
|
||
|
bool bDiff = false;
|
||
|
|
||
|
// Check Files sizes
|
||
|
if (NLMISC::CFile::getFileSize(mainFile) != NLMISC::CFile::getFileSize(mirrorFile))
|
||
|
bDiff = true;
|
||
|
|
||
|
// Check Files (Binary check)
|
||
|
if (!bDiff)
|
||
|
{
|
||
|
uint32 nLength = NLMISC::CFile::getFileSize(mainFile);
|
||
|
CIFile inMain, inMirror;
|
||
|
if (inMain.open(mainFile) && inMirror.open(mirrorFile))
|
||
|
{
|
||
|
uint8 bufferMain[1024];
|
||
|
uint8 bufferMirror[1024];
|
||
|
while (nLength > 0)
|
||
|
{
|
||
|
uint32 r = min(nLength, (uint32)1024);
|
||
|
inMain.serialBuffer(&bufferMain[0], r);
|
||
|
inMirror.serialBuffer(&bufferMirror[0], r);
|
||
|
if (memcmp(bufferMirror,bufferMain,r) != 0)
|
||
|
{
|
||
|
bDiff = true;
|
||
|
break;
|
||
|
}
|
||
|
nLength -= r;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
bDiff = true;
|
||
|
}
|
||
|
|
||
|
if (bDiff)
|
||
|
{
|
||
|
addEntry (Modified, str.c_str (), time1, time0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Update time stamp
|
||
|
FILETIME fileTime;
|
||
|
getFileTime (mainFile.c_str (), fileTime);
|
||
|
setFileTime (mirrorFile.c_str(), fileTime);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
addEntry (Modified, str.c_str (), time1, time0);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FILETIME time;
|
||
|
getFileTime (mainFile.c_str (), time);
|
||
|
addEntry (Added, str.c_str (), time, time);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// List all files in mirror
|
||
|
fileSource.clear ();
|
||
|
CPath::getPathContent (MirrorDirectory+CurrentDir, true, false, true, fileSource);
|
||
|
uint count2 = (uint)fileSource.size ();
|
||
|
for (i=0; i<count2; i++)
|
||
|
{
|
||
|
// Get the filename
|
||
|
string &str = fileSource[i];
|
||
|
str = toLower(str.substr (MirrorDirectory.size (), str.size ()));
|
||
|
|
||
|
// In the ignore list ?
|
||
|
if (IgnoreFiles.find (str) == IgnoreFiles.end () && (str != "ignore_list.txt"))
|
||
|
{
|
||
|
// Does the destination exist ?
|
||
|
string mirrorFile = MirrorDirectory+str;
|
||
|
string mainFile = MainDirectory+str;
|
||
|
if (!NLMISC::CFile::fileExists (mainFile))
|
||
|
{
|
||
|
FILETIME time;
|
||
|
getFileTime (mainFile.c_str (), time);
|
||
|
addEntry (Removed, str.c_str (), time, time);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sort
|
||
|
/*ModifiedList.SortItems ();
|
||
|
AddedList.SortItems ();
|
||
|
RemovedList.SortItems ();*/
|
||
|
|
||
|
UpdateData (TRUE);
|
||
|
}
|
||
|
|
||
|
void timeToString (string &dest, FILETIME time)
|
||
|
{
|
||
|
FILETIME localFileTime;
|
||
|
SYSTEMTIME systemTime;
|
||
|
if (FileTimeToLocalFileTime (&time, &localFileTime))
|
||
|
{
|
||
|
if (FileTimeToSystemTime (&localFileTime, &systemTime))
|
||
|
{
|
||
|
char date[512];
|
||
|
smprintf (date, 512, "%d/%d/%d %d:%02d", systemTime.wDay, systemTime.wMonth, systemTime.wYear, systemTime.wHour, systemTime.wMinute);
|
||
|
dest = date;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sizeToString (string &dest, uint size)
|
||
|
{
|
||
|
char sizeText[512];
|
||
|
smprintf (sizeText, 512, "%d KB", (size / 1024) + ((size%1024) ? 1 : 0));
|
||
|
dest = sizeText;
|
||
|
}
|
||
|
|
||
|
class CExtension
|
||
|
{
|
||
|
public:
|
||
|
string Description;
|
||
|
uint Icon;
|
||
|
};
|
||
|
|
||
|
std::map<string, CExtension> MapExtensions;
|
||
|
|
||
|
void CData_mirrorDlg::addEntry (uint where, const char *filename, FILETIME &newDate, FILETIME &oldDate)
|
||
|
{
|
||
|
// Add an entry first
|
||
|
Files[where].push_back (CEntryFile ());
|
||
|
CEntryFile &file = Files[where].back ();
|
||
|
file.Strings[CEntryFile::Path] = filename;
|
||
|
file.NewDateST = newDate;
|
||
|
file.OldDateST = oldDate;
|
||
|
const char *aFilename;
|
||
|
|
||
|
string mirrorFile = MirrorDirectory+filename;
|
||
|
string mainFile = MainDirectory+filename;
|
||
|
|
||
|
if (where != Added)
|
||
|
{
|
||
|
// Get file size
|
||
|
file.OldSizeUI = NLMISC::CFile::getFileSize (mirrorFile);
|
||
|
sizeToString (file.Strings[CEntryFile::OldSize], file.OldSizeUI);
|
||
|
|
||
|
// Date
|
||
|
timeToString (file.Strings[CEntryFile::OldDate], oldDate);
|
||
|
aFilename = mirrorFile.c_str ();
|
||
|
}
|
||
|
|
||
|
if (where != Removed)
|
||
|
{
|
||
|
// Get file size
|
||
|
file.NewSizeUI = NLMISC::CFile::getFileSize (mainFile);
|
||
|
sizeToString (file.Strings[CEntryFile::NewSize], file.NewSizeUI);
|
||
|
|
||
|
// Date
|
||
|
timeToString (file.Strings[CEntryFile::NewDate], newDate);
|
||
|
aFilename = mainFile.c_str ();
|
||
|
}
|
||
|
|
||
|
// Get the extension
|
||
|
string ext = NLMISC::CFile::getExtension (aFilename);
|
||
|
std::map<string, CExtension>::iterator ite = MapExtensions.find (ext);
|
||
|
if (ite == MapExtensions.end ())
|
||
|
{
|
||
|
// Get the image
|
||
|
SHFILEINFO sfi;
|
||
|
char winName[512];
|
||
|
strcpy (winName, aFilename);
|
||
|
char *ptr = winName;
|
||
|
while (*ptr)
|
||
|
{
|
||
|
if (*ptr=='/')
|
||
|
*ptr = '\\';
|
||
|
ptr++;
|
||
|
}
|
||
|
SHGetFileInfo (winName, 0, &sfi, sizeof (SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_LINKOVERLAY | SHGFI_TYPENAME );
|
||
|
|
||
|
CExtension extension;
|
||
|
extension.Description = sfi.szTypeName;
|
||
|
extension.Icon = sfi.iIcon;
|
||
|
ite = MapExtensions.insert (std::map<string, CExtension>::value_type (ext, extension)).first;
|
||
|
}
|
||
|
|
||
|
file.Strings[CEntryFile::Type] = ite->second.Description;
|
||
|
file.Image = ite->second.Icon;
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnClickList(NMHDR* pNMHDR, LRESULT* pResult)
|
||
|
{
|
||
|
LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) pNMHDR;
|
||
|
|
||
|
UpdateData (FALSE);
|
||
|
UINT flags;
|
||
|
List.HitTest (lpnmlv->ptAction, &flags);
|
||
|
|
||
|
// Get check button state
|
||
|
BOOL state = ListView_GetCheckState (List, lpnmlv->iItem);
|
||
|
if (List.GetItemState (lpnmlv->iItem, LVIS_SELECTED) == LVIS_SELECTED)
|
||
|
if (flags & LVHT_ONITEMSTATEICON)
|
||
|
{
|
||
|
POSITION pos = List.GetFirstSelectedItemPosition();
|
||
|
while (pos)
|
||
|
{
|
||
|
int nItem = List.GetNextSelectedItem(pos);
|
||
|
if (nItem != lpnmlv->iItem)
|
||
|
{
|
||
|
ListView_SetCheckState (List, nItem, !state);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
UpdateData (TRUE);
|
||
|
|
||
|
*pResult = 0;
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnUpdate()
|
||
|
{
|
||
|
UpdateData ();
|
||
|
|
||
|
// Checks
|
||
|
nlassert ((ModifiedFilter>=Modified) && (ModifiedFilter<=Removed));
|
||
|
|
||
|
// The vector to add into
|
||
|
std::vector<string> &entriesToUpdate = FilesToUpdate[ModifiedFilter];
|
||
|
std::list<CEntryFile> &entries = Files[ModifiedFilter];
|
||
|
|
||
|
// Update files
|
||
|
uint count = List.GetItemCount ();
|
||
|
uint i;
|
||
|
for (i=0; i<count; i++)
|
||
|
{
|
||
|
// Checked ?
|
||
|
if (ListView_GetCheckState (List, i))
|
||
|
{
|
||
|
// Get the file
|
||
|
CString itemText = List.GetItemText (i, 0);
|
||
|
|
||
|
// Add to ignore good list
|
||
|
entriesToUpdate.push_back ((const char*)itemText);
|
||
|
|
||
|
// Remove from the file list
|
||
|
std::list<CEntryFile>::iterator *ite = (std::list<CEntryFile>::iterator *)List.GetItemData (i);
|
||
|
nlassert (ite);
|
||
|
entries.erase (*ite);
|
||
|
delete ite;
|
||
|
|
||
|
// Remove the item
|
||
|
List.DeleteItem (i);
|
||
|
i--;
|
||
|
count--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UpdateData (FALSE);
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnAddedFilters()
|
||
|
{
|
||
|
UpdateData ();
|
||
|
SortOrder = true;
|
||
|
SortedColumn = 0;
|
||
|
updateList ();
|
||
|
updateSort ();
|
||
|
IgnoreCtrl.EnableWindow (TRUE);
|
||
|
UpdateData (FALSE);
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnModifiedFilters()
|
||
|
{
|
||
|
UpdateData ();
|
||
|
SortOrder = true;
|
||
|
SortedColumn = 0;
|
||
|
updateList ();
|
||
|
updateSort ();
|
||
|
IgnoreCtrl.EnableWindow (TRUE);
|
||
|
UpdateData (FALSE);
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnRemovedFilters()
|
||
|
{
|
||
|
UpdateData ();
|
||
|
SortOrder = true;
|
||
|
SortedColumn = 0;
|
||
|
updateList ();
|
||
|
updateSort ();
|
||
|
IgnoreCtrl.EnableWindow (FALSE);
|
||
|
UpdateData (FALSE);
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::OnColumnclickList(NMHDR* pNMHDR, LRESULT* pResult)
|
||
|
{
|
||
|
NMLISTVIEW *phdr = (NMLISTVIEW*)pNMHDR;
|
||
|
|
||
|
if (SortedColumn == (uint)phdr->iSubItem)
|
||
|
SortOrder ^= true;
|
||
|
else
|
||
|
SortedColumn = phdr->iSubItem;
|
||
|
|
||
|
updateSort ();
|
||
|
|
||
|
*pResult = 0;
|
||
|
}
|
||
|
|
||
|
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,
|
||
|
LPARAM lParamSort)
|
||
|
{
|
||
|
CData_mirrorDlg *dlg = (CData_mirrorDlg *)lParamSort;
|
||
|
|
||
|
// Get the value pointers
|
||
|
CEntryFile &entry1 = **(list<CEntryFile>::iterator*)lParam1;
|
||
|
CEntryFile &entry2 = **(list<CEntryFile>::iterator*)lParam2;
|
||
|
|
||
|
int result;
|
||
|
switch (dlg->ModifiedFilter)
|
||
|
{
|
||
|
case CData_mirrorDlg::Modified:
|
||
|
{
|
||
|
switch (dlg->SortedColumn)
|
||
|
{
|
||
|
case 0:
|
||
|
result = strcmp (entry1.Strings[CEntryFile::Path].c_str (), entry2.Strings[CEntryFile::Path].c_str ());
|
||
|
break;
|
||
|
case 1:
|
||
|
result = (entry1.NewSizeUI < entry2.NewSizeUI) ? -1 : (entry1.NewSizeUI == entry2.NewSizeUI) ? 0 : 1;
|
||
|
break;
|
||
|
case 2:
|
||
|
result = (entry1.OldSizeUI < entry2.OldSizeUI) ? -1 : (entry1.OldSizeUI == entry2.OldSizeUI) ? 0 : 1;
|
||
|
break;
|
||
|
case 3:
|
||
|
result = CompareFileTime (&entry1.NewDateST, &entry2.NewDateST);
|
||
|
break;
|
||
|
case 4:
|
||
|
result = CompareFileTime (&entry1.OldDateST, &entry2.OldDateST);
|
||
|
break;
|
||
|
case 5:
|
||
|
result = strcmp (entry1.Strings[CEntryFile::Type].c_str (), entry2.Strings[CEntryFile::Type].c_str ());
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case CData_mirrorDlg::Added:
|
||
|
{
|
||
|
switch (dlg->SortedColumn)
|
||
|
{
|
||
|
case 0:
|
||
|
result = strcmp (entry1.Strings[CEntryFile::Path].c_str (), entry2.Strings[CEntryFile::Path].c_str ());
|
||
|
break;
|
||
|
case 1:
|
||
|
result = (entry1.NewSizeUI < entry2.NewSizeUI) ? -1 : (entry1.NewSizeUI == entry2.NewSizeUI) ? 0 : 1;
|
||
|
break;
|
||
|
case 2:
|
||
|
result = CompareFileTime (&entry1.NewDateST, &entry2.NewDateST);
|
||
|
break;
|
||
|
case 3:
|
||
|
result = strcmp (entry1.Strings[CEntryFile::Type].c_str (), entry2.Strings[CEntryFile::Type].c_str ());
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case CData_mirrorDlg::Removed:
|
||
|
{
|
||
|
switch (dlg->SortedColumn)
|
||
|
{
|
||
|
case 0:
|
||
|
result = strcmp (entry1.Strings[CEntryFile::Path].c_str (), entry2.Strings[CEntryFile::Path].c_str ());
|
||
|
break;
|
||
|
case 1:
|
||
|
result = (entry1.OldSizeUI < entry2.OldSizeUI) ? -1 : (entry1.OldSizeUI == entry2.OldSizeUI) ? 0 : 1;
|
||
|
break;
|
||
|
case 2:
|
||
|
result = CompareFileTime (&entry1.OldDateST, &entry2.OldDateST);
|
||
|
break;
|
||
|
case 3:
|
||
|
result = strcmp (entry1.Strings[CEntryFile::Type].c_str (), entry2.Strings[CEntryFile::Type].c_str ());
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return dlg->SortOrder ? result : -result;
|
||
|
}
|
||
|
|
||
|
void CData_mirrorDlg::updateSort ()
|
||
|
{
|
||
|
List.SortItems ( CompareFunc, (LPARAM)this );
|
||
|
}
|