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