1//
2// This file is part of the aMule Project.
3//
4// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6//
7// Any parts of this program derived from the xMule, lMule or eMule project,
8// or contributed by third-party developers are copyrighted by their
9// respective authors.
10//
11// Any parts of this program derived from the xMule, lMule or eMule project,
12// or contributed by third-party developers are copyrighted by their
13// respective authors.
14//
15// This program is free software; you can redistribute it and/or modify
16// it under the terms of the GNU General Public License as published by
17// the Free Software Foundation; either version 2 of the License, or
18// (at your option) any later version.
19//
20// This program is distributed in the hope that it will be useful,
21// but WITHOUT ANY WARRANTY; without even the implied warranty of
22// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23// GNU General Public License for more details.
24//
25// You should have received a copy of the GNU General Public License
26// along with this program; if not, write to the Free Software
27// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
28//
29
30
31#include "muuli_wdr.h"		// Needed for ID_CLOSEWNDFD,...,IDC_APPLY
32#include "FileDetailDialog.h"	// Interface declarations
33#include "FileDetailListCtrl.h"	// Needed for CFileDetailListCtrl
34#include "CommentDialogLst.h"	// Needed for CCommentDialogLst
35#include "PartFile.h"		// Needed for CPartFile
36#include "amule.h"		// Needed for theApp
37#include "SharedFileList.h"	// Needed for CSharedFileList
38#include "OtherFunctions.h"
39#include "MuleColour.h"
40
41#define ID_MY_TIMER 1652
42
43//IMPLEMENT_DYNAMIC(CFileDetailDialog, CDialog)
44BEGIN_EVENT_TABLE(CFileDetailDialog,wxDialog)
45	EVT_BUTTON(ID_CLOSEWNDFD, CFileDetailDialog::OnClosewnd)
46	EVT_BUTTON(IDC_BUTTONSTRIP, CFileDetailDialog::OnBnClickedButtonStrip)
47	EVT_BUTTON(IDC_TAKEOVER, CFileDetailDialog::OnBnClickedTakeOver)
48	EVT_LIST_ITEM_ACTIVATED(IDC_LISTCTRLFILENAMES, CFileDetailDialog::OnListClickedTakeOver)
49	EVT_BUTTON(IDC_CMTBT, CFileDetailDialog::OnBnClickedShowComment)
50	EVT_TEXT(IDC_FILENAME, CFileDetailDialog::OnTextFileNameChange)
51	EVT_BUTTON(IDC_APPLY_AND_CLOSE, CFileDetailDialog::OnBnClickedOk)
52	EVT_BUTTON(IDC_APPLY, CFileDetailDialog::OnBnClickedApply)
53	EVT_BUTTON(IDC_PREVFILE, CFileDetailDialog::OnBnClickedPrevFile)
54	EVT_BUTTON(IDC_NEXTFILE, CFileDetailDialog::OnBnClickedNextFile)
55	EVT_TIMER(ID_MY_TIMER,CFileDetailDialog::OnTimer)
56END_EVENT_TABLE()
57
58CFileDetailDialog::CFileDetailDialog(wxWindow *parent, std::vector<CPartFile *> & files, int index)
59:
60wxDialog(parent, -1, _("File Details"), wxDefaultPosition, wxDefaultSize,
61	wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX),
62m_files(files),
63m_index(index),
64m_filenameChanged(false)
65{
66	theApp->m_FileDetailDialogActive++;
67	m_timer.SetOwner(this, ID_MY_TIMER);
68	m_timer.Start(5000);
69	wxSizer *content = fileDetails(this, true);
70	m_file = m_files[m_index];
71	UpdateData(true);
72	content->SetSizeHints(this);
73	content->Show(this, true);
74}
75
76CFileDetailDialog::~CFileDetailDialog()
77{
78	theApp->m_FileDetailDialogActive = 0;
79	m_timer.Stop();
80}
81
82void CFileDetailDialog::OnTimer(wxTimerEvent& WXUNUSED(evt))
83{
84	UpdateData(false);
85}
86
87void CFileDetailDialog::OnClosewnd(wxCommandEvent& WXUNUSED(evt))
88{
89	EndModal(0);
90}
91
92void CFileDetailDialog::UpdateData(bool resetFilename)
93{
94	wxString bufferS;
95	CastChild(IDC_FNAME,   wxStaticText)->SetLabel(MakeStringEscaped(m_file->GetFileName().TruncatePath(60)));
96	CastChild(IDC_METFILE, wxStaticText)->SetLabel(MakeStringEscaped(m_file->GetFullName().TruncatePath(60, true)));
97
98	if (resetFilename) {
99		resetValueForFilenameTextEdit();
100	}
101
102	CastChild(IDC_FHASH,wxStaticText)->SetLabel(m_file->GetFileHash().Encode());
103	bufferS = CFormat(wxT("%u bytes (%s)")) % m_file->GetFileSize() % CastItoXBytes(m_file->GetFileSize());
104	CastChild(IDC_FSIZE,wxControl)->SetLabel(bufferS);
105	CastChild(IDC_PFSTATUS,wxControl)->SetLabel(m_file->getPartfileStatus());
106	bufferS = CFormat(wxT("%i (%i)")) % m_file->GetPartCount() % m_file->GetHashCount();
107	CastChild(IDC_PARTCOUNT,wxControl)->SetLabel(bufferS);
108	CastChild(IDC_TRANSFERRED,wxControl)->SetLabel(CastItoXBytes(m_file->GetTransferred()));
109	CastChild(IDC_FD_STATS1,wxControl)->SetLabel(CastItoXBytes(m_file->GetLostDueToCorruption()));
110	CastChild(IDC_FD_STATS2,wxControl)->SetLabel(CastItoXBytes(m_file->GetGainDueToCompression()));
111	CastChild(IDC_FD_STATS3,wxControl)->SetLabel(CastItoIShort(m_file->TotalPacketsSavedDueToICH()));
112	CastChild(IDC_COMPLSIZE,wxControl)->SetLabel(CastItoXBytes(m_file->GetCompletedSize()));
113	bufferS = CFormat(_("%.1f%% done")) % m_file->GetPercentCompleted();
114	CastChild(IDC_PROCCOMPL,wxControl)->SetLabel(bufferS);
115	bufferS = CFormat(_("%.2f kB/s")) % m_file->GetKBpsDown();
116	CastChild(IDC_DATARATE,wxControl)->SetLabel(bufferS);
117	bufferS = CFormat(wxT("%i")) % m_file->GetSourceCount();
118	CastChild(IDC_SOURCECOUNT,wxControl)->SetLabel(bufferS);
119	bufferS = CFormat(wxT("%i")) % m_file->GetTransferingSrcCount();
120	CastChild(IDC_SOURCECOUNT2,wxControl)->SetLabel(bufferS);
121	bufferS = CFormat(wxT("%i (%.1f%%)"))
122		% m_file->GetAvailablePartCount()
123		% ((m_file->GetAvailablePartCount() * 100.0)/ m_file->GetPartCount());
124	CastChild(IDC_PARTAVAILABLE,wxControl)->SetLabel(bufferS);
125	bufferS = CastSecondsToHM(m_file->GetDlActiveTime());
126	CastChild(IDC_DLACTIVETIME, wxControl)->SetLabel(bufferS);
127
128	if (m_file->lastseencomplete==0) {
129		bufferS = wxString(_("Unknown")).MakeLower();
130	} else {
131		wxDateTime last_seen(m_file->lastseencomplete);
132		bufferS = last_seen.FormatISODate() + wxT(" ") + last_seen.FormatISOTime();
133	}
134
135	CastChild(IDC_LASTSEENCOMPL,wxControl)->SetLabel(bufferS);
136	setEnableForApplyButton();
137	// disable "Show all comments" button if there are no comments
138	FileRatingList list;
139	m_file->GetRatingAndComments(list);
140	CastChild(IDC_CMTBT, wxControl)->Enable(!list.empty());
141	FillSourcenameList();
142	Layout();
143}
144
145// CFileDetailDialog message handlers
146
147void CFileDetailDialog::FillSourcenameList()
148{
149	CFileDetailListCtrl* pmyListCtrl;
150	int itempos;
151	int inserted = 0;
152	pmyListCtrl = CastChild(IDC_LISTCTRLFILENAMES, CFileDetailListCtrl );
153
154	// reset
155	for (int i=0;i<pmyListCtrl->GetItemCount();i++){
156		SourcenameItem *item = reinterpret_cast<SourcenameItem *>(pmyListCtrl->GetItemData(i));
157		item->count = 0;
158	}
159
160	// update
161#ifdef CLIENT_GUI
162	const SourcenameItemMap &sources = m_file->GetSourcenameItemMap();
163	for (SourcenameItemMap::const_iterator it = sources.begin(); it != sources.end(); ++it) {
164		const SourcenameItem &cur_src = it->second;
165		itempos = pmyListCtrl->FindItem(-1,cur_src.name);
166		if (itempos == -1) {
167			int itemid = pmyListCtrl->InsertItem(0, cur_src.name);
168			SourcenameItem *item = new SourcenameItem(cur_src.name, cur_src.count);
169			pmyListCtrl->SetItemPtrData(0, reinterpret_cast<wxUIntPtr>(item));
170			// background.. argh -- PA: was in old version - do we still need this?
171			wxListItem tmpitem;
172			tmpitem.m_itemId = itemid;
173			tmpitem.SetBackgroundColour(CMuleColour(wxSYS_COLOUR_LISTBOX));
174			pmyListCtrl->SetItem(tmpitem);
175			inserted++;
176		} else {
177			SourcenameItem *item = reinterpret_cast<SourcenameItem *>(pmyListCtrl->GetItemData(itempos));
178			item->count = cur_src.count;
179		}
180	}
181#else // CLIENT_GUI
182	const CKnownFile::SourceSet& sources = m_file->GetSourceList();
183	CKnownFile::SourceSet::const_iterator it = sources.begin();
184	for ( ; it != sources.end(); ++it ) {
185		const CClientRef &cur_src = *it;
186		if (cur_src.GetRequestFile() != m_file ||
187		    cur_src.GetClientFilename().Length() == 0) {
188			continue;
189		}
190
191		itempos = pmyListCtrl->FindItem(-1,cur_src.GetClientFilename());
192		if (itempos == -1) {
193			int itemid = pmyListCtrl->InsertItem(0, cur_src.GetClientFilename());
194			SourcenameItem *item = new SourcenameItem(cur_src.GetClientFilename(), 1);
195			pmyListCtrl->SetItemPtrData(0, reinterpret_cast<wxUIntPtr>(item));
196			// background.. argh -- PA: was in old version - do we still need this?
197			wxListItem tmpitem;
198			tmpitem.m_itemId=itemid;
199			tmpitem.SetBackgroundColour(CMuleColour(wxSYS_COLOUR_LISTBOX));
200			pmyListCtrl->SetItem(tmpitem);
201			inserted++;
202		} else {
203			SourcenameItem *item = reinterpret_cast<SourcenameItem *>(pmyListCtrl->GetItemData(itempos));
204			item->count++;
205		}
206	}
207#endif // CLIENT_GUI
208
209	// remove 0'er and update counts
210	for (int i = 0; i < pmyListCtrl->GetItemCount(); ++i) {
211		SourcenameItem *item = reinterpret_cast<SourcenameItem *>(pmyListCtrl->GetItemData(i));
212		if (item->count == 0) {
213			delete item;
214			pmyListCtrl->DeleteItem(i);
215			i--;  // PA: one step back is enough, no need to go back to 0
216		} else {
217			pmyListCtrl->SetItem(i, 1, CFormat(wxT("%i")) % item->count);
218		}
219	}
220
221	if (inserted) {
222		pmyListCtrl->SortList();
223	}
224	// no need to call Layout() here, it's called in UpdateData()
225}
226
227
228void CFileDetailDialog::OnBnClickedShowComment(wxCommandEvent& WXUNUSED(evt))
229{
230	CCommentDialogLst(this,m_file).ShowModal();
231}
232
233
234void CFileDetailDialog::resetValueForFilenameTextEdit()
235{
236	CastChild(IDC_FILENAME, wxTextCtrl)->SetValue(m_file->GetFileName().GetPrintable());
237	m_filenameChanged = false;
238	setEnableForApplyButton();
239}
240
241
242void CFileDetailDialog::setValueForFilenameTextEdit(const wxString &s)
243{
244	CastChild(IDC_FILENAME, wxTextCtrl)->SetValue(s);
245	m_filenameChanged = true;
246	setEnableForApplyButton();
247}
248
249
250void CFileDetailDialog::setEnableForApplyButton()
251{
252	bool enabled =
253		m_file->GetStatus() != PS_COMPLETE &&
254		m_file->GetStatus() != PS_COMPLETING &&
255		m_filenameChanged;
256	CastChild(IDC_APPLY, wxControl)->Enable(enabled);
257	// Make OK button default so Text can be applied by hitting return
258	CastChild(enabled ? IDC_APPLY_AND_CLOSE : ID_CLOSEWNDFD, wxButton)->SetDefault();
259}
260
261
262void CFileDetailDialog::OnTextFileNameChange(wxCommandEvent& WXUNUSED(evt))
263{
264	m_filenameChanged = true;
265	setEnableForApplyButton();
266}
267
268
269void CFileDetailDialog::OnBnClickedOk(wxCommandEvent& evt)
270{
271	OnBnClickedApply(evt);
272	OnClosewnd(evt);
273}
274
275
276void CFileDetailDialog::OnBnClickedApply(wxCommandEvent& WXUNUSED(evt))
277{
278	CPath fileName = CPath(CastChild(IDC_FILENAME, wxTextCtrl)->GetValue());
279
280	if (fileName.IsOk() && (fileName != m_file->GetFileName())) {
281		if (theApp->sharedfiles->RenameFile(m_file, fileName)) {
282			FindWindow(IDC_FNAME)->SetLabel(MakeStringEscaped(m_file->GetFileName().GetPrintable()));
283			FindWindow(IDC_METFILE)->SetLabel(m_file->GetFullName().GetPrintable());
284
285			resetValueForFilenameTextEdit();
286
287			Layout();
288		}
289	}
290}
291
292
293void CFileDetailDialog::OnBnClickedPrevFile(wxCommandEvent&)
294{
295	if (--m_index < 0) {
296		m_index = m_files.size() - 1;
297	}
298	m_file = m_files[m_index];
299	UpdateData(true);
300}
301
302
303void CFileDetailDialog::OnBnClickedNextFile(wxCommandEvent&)
304{
305	if (++m_index == (int) m_files.size()) {
306		m_index = 0;
307	}
308	m_file = m_files[m_index];
309	UpdateData(true);
310}
311
312
313bool IsDigit(const wxChar ch)
314{
315	switch (ch) {
316		case '0':
317		case '1':
318		case '2':
319		case '3':
320		case '4':
321		case '5':
322		case '6':
323		case '7':
324		case '8':
325		case '9': return true;
326	}
327	return false;
328}
329
330bool IsWordSeparator(const wxChar ch)
331{
332	switch (ch) {
333		case '.':
334		case ',':
335		case '(':
336		case ')':
337		case '[':
338		case ']':
339		case '{':
340		case '}':
341		case '-':
342		case '"':
343		case ' ': return true;
344	}
345	return false;
346}
347
348void ReplaceWord(wxString& str, const wxString& replaceFrom, const wxString& replaceTo, bool numbers = false)
349{
350	unsigned int i = 0;
351	unsigned int l = replaceFrom.Length();
352	while (i < str.Length()) {
353		if (str.Mid(i, l) == replaceFrom) {
354			if ((i == 0 || IsWordSeparator(str.GetChar(i-1))) &&
355				((i == str.Length() - l || IsWordSeparator(str.GetChar(i+l))) ||
356					(numbers && IsDigit(str.GetChar(i+l))))) {
357				str.replace(i, l, replaceTo);
358			}
359			i += replaceTo.Length() - 1;
360		}
361		i++;
362	}
363}
364
365void CFileDetailDialog::OnBnClickedButtonStrip(wxCommandEvent& WXUNUSED(evt))
366{
367	wxString filename;
368	filename = CastChild(IDC_FILENAME, wxTextCtrl)->GetValue();
369
370	int extpos = filename.Find('.', true);
371	wxString ext;
372	if (extpos > 0) {
373		// get the extension - we do not modify it except make it lowercase
374		ext = filename.Mid(extpos);
375		ext.MakeLower();
376		// get rid of extension and replace . with space
377		filename.Truncate(extpos);
378		filename.Replace(wxT("."),wxT(" "));
379	}
380
381	// Replace Space-holders with Spaces
382	filename.Replace(wxT("_"),wxT(" "));
383	filename.Replace(wxT("%20"),wxT(" "));
384
385	// Some additional formatting
386	filename.Replace(wxT("hYPNOTiC"), wxEmptyString);
387	filename.MakeLower();
388	filename.Replace(wxT("xxx"), wxT("XXX"));
389//	filename.Replace(wxT("xdmnx"), wxEmptyString);
390//	filename.Replace(wxT("pmp"), wxEmptyString);
391//	filename.Replace(wxT("dws"), wxEmptyString);
392	filename.Replace(wxT("www pornreactor com"), wxEmptyString);
393	filename.Replace(wxT("sharereactor"), wxEmptyString);
394	filename.Replace(wxT("found via www filedonkey com"), wxEmptyString);
395	filename.Replace(wxT("deviance"), wxEmptyString);
396	filename.Replace(wxT("adunanza"), wxEmptyString);
397	filename.Replace(wxT("-ftv"), wxEmptyString);
398	filename.Replace(wxT("flt"), wxEmptyString);
399	filename.Replace(wxT("[]"), wxEmptyString);
400	filename.Replace(wxT("()"), wxEmptyString);
401
402	// Change CD, CD#, VCD{,#}, DVD{,#}, ISO, PC to uppercase
403	ReplaceWord(filename, wxT("cd"), wxT("CD"), true);
404	ReplaceWord(filename, wxT("vcd"), wxT("VCD"), true);
405	ReplaceWord(filename, wxT("dvd"), wxT("DVD"), true);
406	ReplaceWord(filename, wxT("iso"), wxT("ISO"), false);
407	ReplaceWord(filename, wxT("pc"), wxT("PC"), false);
408
409	// Make leading Caps
410	// and delete 1+ spaces
411	if (filename.Length()>1)
412	{
413		bool last_char_space = true;
414		bool last_char_wordseparator = true;
415		unsigned int i = 0;
416
417		do {
418			wxChar c = filename.GetChar(i);
419			if (c == ' ') {
420				if (last_char_space) {
421					filename.Remove(i, 1);
422					i--;
423				} else {
424					last_char_space = true;
425				}
426			} else if (c == '.') {
427				if (last_char_space && i > 0) {
428					i--;
429					filename.Remove(i, 1);
430				}
431				last_char_space = false;
432			} else {
433				if (last_char_wordseparator) {
434					wxString tempStr(c);
435					tempStr.MakeUpper();
436					filename.SetChar(i, tempStr.GetChar(0));
437					last_char_space = false;
438				}
439			}
440			last_char_wordseparator = IsWordSeparator(c);
441			i++;
442		} while (i < filename.Length());
443
444		if (last_char_space && i > 0) {
445			filename.Remove(i-1, 1);
446		}
447	}
448
449	// should stay lowercase
450	ReplaceWord(filename, wxT("By"), wxT("by"));
451
452	// re-add extension
453	filename += ext;
454
455	setValueForFilenameTextEdit(filename);
456}
457
458void CFileDetailDialog::OnBnClickedTakeOver(wxCommandEvent& WXUNUSED(evt))
459{
460	CFileDetailListCtrl* pmyListCtrl;
461	pmyListCtrl = CastChild( IDC_LISTCTRLFILENAMES, CFileDetailListCtrl );
462	if (pmyListCtrl->GetSelectedItemCount() > 0) {
463		// get first selected item (there is only one)
464		long pos = pmyListCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
465		if (pos != -1) {	// shouldn't happen, we checked if something is selected
466			setValueForFilenameTextEdit(pmyListCtrl->GetItemText(pos));
467		}
468	}
469}
470
471void CFileDetailDialog::OnListClickedTakeOver(wxListEvent& WXUNUSED(evt))
472{
473	wxCommandEvent ev;
474	OnBnClickedTakeOver(ev);
475}
476// File_checked_for_headers
477