Fixed problem in the CFile write_safe concept
[amule.git] / src / SearchListCtrl.cpp
blob4d7683e84f5cb18da7cf23ac6c4a2477bf0daa96
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.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "SearchListCtrl.h" // Interface declarations
28 #include <common/MenuIDs.h>
30 #include "amule.h" // Needed for theApp
31 #include "KnownFileList.h" // Needed for CKnownFileList
32 #include "SearchList.h" // Needed for CSearchFile
33 #include "SearchDlg.h" // Needed for CSearchDlg
34 #include "amuleDlg.h" // Needed for CamuleDlg
35 #include "muuli_wdr.h" // Needed for clientImages
36 #include "Preferences.h" // Needed for thePrefs
37 #include "GuiEvents.h" // Needed for CoreNotify_Search_Add_Download
38 #include "MuleColour.h"
40 BEGIN_EVENT_TABLE(CSearchListCtrl, CMuleListCtrl)
41 EVT_LIST_ITEM_RIGHT_CLICK(-1, CSearchListCtrl::OnRightClick)
42 EVT_LIST_COL_CLICK( -1, CSearchListCtrl::OnColumnLClick)
43 EVT_LIST_COL_END_DRAG( -1, CSearchListCtrl::OnColumnResize)
45 EVT_MENU( MP_GETED2KLINK, CSearchListCtrl::OnPopupGetUrl)
46 EVT_MENU( MP_RAZORSTATS, CSearchListCtrl::OnRazorStatsCheck)
47 EVT_MENU( MP_SEARCHRELATED, CSearchListCtrl::OnRelatedSearch)
48 EVT_MENU( MP_MARK_AS_KNOWN, CSearchListCtrl::OnMarkAsKnown)
49 EVT_MENU( MP_RESUME, CSearchListCtrl::OnPopupDownload)
50 EVT_MENU_RANGE( MP_ASSIGNCAT, MP_ASSIGNCAT + 99, CSearchListCtrl::OnPopupDownload )
52 EVT_LIST_ITEM_ACTIVATED( -1, CSearchListCtrl::OnItemActivated)
53 END_EVENT_TABLE()
56 std::list<CSearchListCtrl*> CSearchListCtrl::s_lists;
59 enum SearchListColumns {
60 ID_SEARCH_COL_NAME = 0,
61 ID_SEARCH_COL_SIZE,
62 ID_SEARCH_COL_SOURCES,
63 ID_SEARCH_COL_TYPE,
64 ID_SEARCH_COL_FILEID,
65 ID_SEARCH_COL_STATUS
69 CSearchListCtrl::CSearchListCtrl(
70 wxWindow *parent,
71 wxWindowID winid,
72 const wxPoint &pos,
73 const wxSize &size,
74 long style,
75 const wxValidator &validator,
76 const wxString &name)
78 CMuleListCtrl(parent, winid, pos, size, style | wxLC_OWNERDRAW, validator, name),
79 m_filterKnown(false),
80 m_invert(false),
81 m_filterEnabled(false)
83 // Setting the sorter function.
84 SetSortFunc( SortProc );
86 InsertColumn( ID_SEARCH_COL_NAME, _("File Name"), wxLIST_FORMAT_LEFT, 500, wxT("N") );
87 InsertColumn( ID_SEARCH_COL_SIZE, _("Size"), wxLIST_FORMAT_LEFT, 100, wxT("Z") );
88 InsertColumn( ID_SEARCH_COL_SOURCES, _("Sources"), wxLIST_FORMAT_LEFT, 50, wxT("u") );
89 InsertColumn( ID_SEARCH_COL_TYPE, _("Type"), wxLIST_FORMAT_LEFT, 65, wxT("Y") );
90 InsertColumn( ID_SEARCH_COL_FILEID, _("FileID"), wxLIST_FORMAT_LEFT, 280, wxT("I") );
91 InsertColumn( ID_SEARCH_COL_STATUS, _("Status"), wxLIST_FORMAT_LEFT, 100, wxT("S") );
93 m_nResultsID = 0;
95 // Only load settings for first list, otherwise sync with current lists
96 if ( s_lists.empty() ) {
97 // Set the name to enable loading of settings
98 SetTableName( wxT("Search") );
100 LoadSettings();
102 // Unset the name to avoid the settings getting saved every time a list is closed
103 SetTableName( wxEmptyString );
104 } else {
105 // Sync this list with one of the others
106 SyncLists( s_lists.front(), this );
109 // Add the list so that it will be synced with the other lists
110 s_lists.push_back( this );
114 wxString CSearchListCtrl::GetOldColumnOrder() const
116 return wxT("N,Z,u,Y,I,S");
120 CSearchListCtrl::~CSearchListCtrl()
122 std::list<CSearchListCtrl*>::iterator it = std::find( s_lists.begin(), s_lists.end(), this );
124 if ( it != s_lists.end() )
125 s_lists.erase( it );
127 // We only save the settings if the last list was closed
128 if ( s_lists.empty() ) {
129 // In order to get the settings saved, we need to set the name
130 SetTableName( wxT("Search") );
135 void CSearchListCtrl::AddResult(CSearchFile* toshow)
137 wxCHECK_RET(toshow->GetSearchID() == m_nResultsID, wxT("Wrong search-id for result-list"));
139 const wxUIntPtr toshowdata = reinterpret_cast<wxUIntPtr>(toshow);
140 CSearchFile* parent = toshow->GetParent();
142 // Check if the result should be shown
143 if (FindItem(-1, toshowdata) != -1) {
144 return;
145 } else if (parent && !parent->ShowChildren()) {
146 return;
147 } else if (!IsFiltered(toshow)) {
148 if (toshow->HasChildren() && toshow->ShowChildren()) {
149 // Only filter the parent if none of the children are shown.
150 bool foundChild = false;
151 const CSearchResultList& children = toshow->GetChildren();
152 for (size_t i = 0; i < children.size(); ++i) {
153 if (IsFiltered(children.at(i))) {
154 foundChild = true;
155 break;
159 if (!foundChild) {
160 // No children left, and the parent is filtered.
161 m_filteredOut.push_back(toshow);
162 return;
164 } else {
165 m_filteredOut.push_back(toshow);
166 return;
170 // Insert the item before the item found by the search
171 long insertPos;
172 if (parent) {
173 insertPos = FindItem(-1, (wxUIntPtr)parent);
174 if (insertPos == -1) {
175 wxFAIL;
176 insertPos = GetItemCount();
177 } else {
178 insertPos++;
180 } else {
181 insertPos = GetInsertPos(toshowdata);
183 long newid = InsertItem(insertPos, toshow->GetFileName().GetPrintable());
185 // Sanity checks to ensure that results/children are properly positioned.
186 #ifdef __WXDEBUG__
188 if (newid > 0) {
189 CSearchFile* before = (CSearchFile*)GetItemData(newid - 1);
190 wxASSERT(before);
191 if (parent) {
192 wxASSERT((before->GetParent() == parent) || (before == parent));
193 } else {
194 wxASSERT(before->GetParent() != toshow);
198 if ((int)newid < GetItemCount() - 1) {
199 CSearchFile* after = (CSearchFile*)GetItemData(newid + 1);
200 wxASSERT(after);
201 if (parent) {
202 wxASSERT((after->GetParent() == parent) || (!after->GetParent()));
203 } else {
204 wxASSERT((after->GetParent() == toshow) || (!after->GetParent()));
208 #endif
210 SetItemPtrData(newid, toshowdata);
212 // Filesize
213 SetItem(newid, ID_SEARCH_COL_SIZE, CastItoXBytes( toshow->GetFileSize() ) );
215 // Source count
216 wxString temp = CFormat(wxT("%d")) % toshow->GetSourceCount();
217 if (toshow->GetCompleteSourceCount()) {
218 temp += CFormat(wxT(" (%d)")) % toshow->GetCompleteSourceCount();
220 if (toshow->GetClientsCount()) {
221 temp += CFormat(wxT(" [%d]")) % toshow->GetClientsCount();
223 #if defined(__DEBUG__) && !defined(CLIENT_GUI)
224 if (toshow->GetKadPublishInfo() == 0) {
225 temp += wxT(" | -");
226 } else {
227 temp += CFormat(wxT(" | N:%u, P:%u, T:%0.2f"))
228 % ((toshow->GetKadPublishInfo() & 0xFF000000) >> 24)
229 % ((toshow->GetKadPublishInfo() & 0x00FF0000) >> 16)
230 % ((toshow->GetKadPublishInfo() & 0x0000FFFF) / 100.0);
232 #endif
233 SetItem( newid, ID_SEARCH_COL_SOURCES, temp );
235 // File-type
236 SetItem( newid, ID_SEARCH_COL_TYPE, GetFiletypeByName( toshow->GetFileName() ) );
238 // File-hash
239 SetItem(newid, ID_SEARCH_COL_FILEID, toshow->GetFileHash().Encode() );
241 // File status
242 SetItem(newid, ID_SEARCH_COL_STATUS, DetermineStatusPrintable(toshow));
244 // Set the color of the item
245 UpdateItemColor( newid );
249 void CSearchListCtrl::RemoveResult(CSearchFile* toremove)
251 ShowChildren(toremove, false);
253 long index = FindItem(-1, reinterpret_cast<wxUIntPtr>(toremove));
254 if (index != -1) {
255 DeleteItem(index);
256 } else {
257 ResultList::iterator it = std::find(m_filteredOut.begin(), m_filteredOut.end(), toremove);
258 if ( it != m_filteredOut.end()) {
259 m_filteredOut.erase(it);
265 void CSearchListCtrl::UpdateResult(CSearchFile* toupdate)
267 long index = FindItem(-1, reinterpret_cast<wxUIntPtr>(toupdate));
268 if (index != -1) {
269 // Update the filename, which may be changed in case of multiple variants.
270 SetItem(index, ID_SEARCH_COL_NAME, toupdate->GetFileName().GetPrintable());
272 wxString temp = CFormat(wxT("%d")) % toupdate->GetSourceCount();
273 if (toupdate->GetCompleteSourceCount()) {
274 temp += CFormat(wxT(" (%d)")) % toupdate->GetCompleteSourceCount();
276 if (toupdate->GetClientsCount()) {
277 temp += CFormat(wxT(" [%d]")) % toupdate->GetClientsCount();
279 #if defined(__DEBUG__) && !defined(CLIENT_GUI)
280 if (toupdate->GetKadPublishInfo() == 0) {
281 temp += wxT(" | -");
282 } else {
283 temp += CFormat(wxT(" | N:%u, P:%u, T:%0.2f"))
284 % ((toupdate->GetKadPublishInfo() & 0xFF000000) >> 24)
285 % ((toupdate->GetKadPublishInfo() & 0x00FF0000) >> 16)
286 % ((toupdate->GetKadPublishInfo() & 0x0000FFFF) / 100.0);
288 #endif
289 SetItem(index, ID_SEARCH_COL_SOURCES, temp);
291 SetItem(index, ID_SEARCH_COL_STATUS, DetermineStatusPrintable(toupdate));
293 UpdateItemColor(index);
295 // Deletions of items causes rather large amount of flicker, so to
296 // avoid this, we resort the list to ensure correct ordering.
297 if (!IsItemSorted(index)) {
298 SortList();
304 void CSearchListCtrl::UpdateItemColor(long index)
306 wxListItem item;
307 item.SetId( index );
308 item.SetColumn( ID_SEARCH_COL_SIZE );
309 item.SetMask(
310 wxLIST_MASK_STATE |
311 wxLIST_MASK_TEXT |
312 wxLIST_MASK_IMAGE |
313 wxLIST_MASK_DATA |
314 wxLIST_MASK_WIDTH |
315 wxLIST_MASK_FORMAT);
317 if (GetItem(item)) {
318 CMuleColour newcol(wxSYS_COLOUR_WINDOWTEXT);
320 CSearchFile* file = (CSearchFile*)GetItemData(index);
322 int red = newcol.Red();
323 int green = newcol.Green();
324 int blue = newcol.Blue();
326 switch (file->GetDownloadStatus()) {
327 case CSearchFile::DOWNLOADED:
328 // File has already been downloaded. Mark as green.
329 green = 255;
330 break;
331 case CSearchFile::QUEUED:
332 // File is downloading.
333 case CSearchFile::QUEUEDCANCELED:
334 // File is downloading and has been canceled before.
335 // Mark as red
336 red = 255;
337 break;
338 case CSearchFile::CANCELED:
339 // File has been canceled. Mark as magenta.
340 red = 255;
341 blue = 255;
342 break;
343 default:
344 // File is new, colour after number of files
345 blue += file->GetSourceCount() * 5;
346 if ( blue > 255 ) {
347 blue = 255;
351 // don't forget to set the item data back...
352 wxListItem newitem;
353 newitem.SetId( index );
354 newitem.SetTextColour( wxColour( red, green, blue ) );
355 SetItem( newitem );
360 void CSearchListCtrl::ShowResults( long ResultsID )
362 DeleteAllItems();
363 m_nResultsID = ResultsID;
364 if (ResultsID) {
365 const CSearchResultList& list = theApp->searchlist->GetSearchResults(ResultsID);
366 for (unsigned int i = 0; i < list.size(); ++i) {
367 AddResult( list[i] );
373 wxUIntPtr CSearchListCtrl::GetSearchId()
375 return m_nResultsID;
379 void CSearchListCtrl::SetFilter(const wxString& regExp, bool invert, bool filterKnown)
381 if (regExp.IsEmpty()) {
382 // Show everything
383 m_filterText = wxT(".*");
384 } else {
385 m_filterText = regExp;
388 m_filter.Compile(m_filterText, wxRE_DEFAULT | wxRE_ICASE);
389 m_filterKnown = filterKnown;
390 m_invert = invert;
392 if (m_filterEnabled) {
393 // Swap the list of filtered results so we can freely add new items to the list
394 ResultList curFiltered;
395 std::swap(curFiltered, m_filteredOut);
397 // Filter items already on the list
398 for (int i = 0; i < GetItemCount();) {
399 CSearchFile* file = (CSearchFile*)GetItemData(i);
401 if (IsFiltered(file)) {
402 ++i;
403 } else {
404 m_filteredOut.push_back(file);
405 DeleteItem(i);
409 // Check the previously filtered items.
410 ResultList::iterator it = curFiltered.begin();
411 for (; it != curFiltered.end(); ++it) {
412 if (IsFiltered(*it)) {
413 AddResult(*it);
414 } else {
415 m_filteredOut.push_back(*it);
422 void CSearchListCtrl::EnableFiltering(bool enabled)
424 if (enabled != m_filterEnabled) {
425 m_filterEnabled = enabled;
427 if (enabled) {
428 SetFilter(m_filterText, m_invert, m_filterKnown);
429 } else {
430 ResultList::iterator it = m_filteredOut.begin();
431 for (; it != m_filteredOut.end(); ++it) {
432 AddResult(*it);
435 m_filteredOut.clear();
441 size_t CSearchListCtrl::GetHiddenItemCount() const
443 return m_filteredOut.size();
447 bool CSearchListCtrl::IsFiltered(const CSearchFile* file)
449 // By default, everything is displayed
450 bool result = true;
452 if (m_filterEnabled && m_filter.IsValid()) {
453 result = m_filter.Matches(file->GetFileName().GetPrintable());
454 result = ((result && !m_invert) || (!result && m_invert));
455 if (result && m_filterKnown) {
456 result = file->GetDownloadStatus() == CSearchFile::NEW;
460 return result;
464 int CSearchListCtrl::SortProc(wxUIntPtr item1, wxUIntPtr item2, long sortData)
466 CSearchFile* file1 = (CSearchFile*)item1;
467 CSearchFile* file2 = (CSearchFile*)item2;
469 // Modifies the result, 1 for ascending, -1 for decending
470 int modifier = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
471 bool alternate = (sortData & CMuleListCtrl::SORT_ALT) != 0;
473 // Decide if which should files we should sort by.
474 wxUIntPtr parent1 = reinterpret_cast<wxUIntPtr>(file1->GetParent());
475 wxUIntPtr parent2 = reinterpret_cast<wxUIntPtr>(file2->GetParent());
476 wxUIntPtr filePtr1 = reinterpret_cast<wxUIntPtr>(file1);
477 wxUIntPtr filePtr2 = reinterpret_cast<wxUIntPtr>(file2);
478 if (parent1 && parent2) {
479 if (parent1 != parent2) {
480 return SortProc(parent1, parent2, sortData);
482 } else if (parent1) {
483 if (parent1 == filePtr2) {
484 return 1;
485 } else {
486 return SortProc(parent1, filePtr2, sortData);
488 } else if (parent2) {
489 if (parent2 == filePtr1) {
490 return -1;
491 } else {
492 return SortProc(filePtr1, parent2, sortData);
496 int result = 0;
497 switch (sortData & CMuleListCtrl::COLUMN_MASK) {
498 // Sort by filename
499 case ID_SEARCH_COL_NAME:
500 result = CmpAny(file1->GetFileName(), file2->GetFileName());
501 break;
503 // Sort file-size
504 case ID_SEARCH_COL_SIZE:
505 result = CmpAny( file1->GetFileSize(), file2->GetFileSize() );
506 break;
508 // Sort by sources
509 case ID_SEARCH_COL_SOURCES: {
510 int cmp = CmpAny( file1->GetSourceCount(), file2->GetSourceCount() );
511 int cmp2 = CmpAny( file1->GetCompleteSourceCount(), file2->GetCompleteSourceCount() );
513 if ( alternate ) {
514 // Swap criterias
515 int temp = cmp2;
516 cmp2 = cmp;
517 cmp = temp;
520 if ( cmp == 0 ) {
521 cmp = cmp2;
524 result = cmp;
525 break;
528 // Sort by file-types
529 case ID_SEARCH_COL_TYPE: {
530 result = GetFiletypeByName(file1->GetFileName()).Cmp(GetFiletypeByName(file2->GetFileName()));
531 if (result == 0) {
532 // Same file-type, sort by extension
533 result = CmpAny(file1->GetFileName().GetExt(), file2->GetFileName().GetExt());
536 break;
539 // Sort by file-hash
540 case ID_SEARCH_COL_FILEID:
541 result = CmpAny(file2->GetFileHash(), file1->GetFileHash());
542 break;
544 // Sort by file status
545 case ID_SEARCH_COL_STATUS:
546 result = CmpAny(DetermineStatusPrintable(file2), DetermineStatusPrintable(file1));
547 break;
550 return modifier * result;
554 void CSearchListCtrl::SetSorting(unsigned column, unsigned order)
556 Freeze();
557 // First collapse all parent items
558 // Backward order means our index won't be influenced by items getting collapsed.
559 for (int i = GetItemCount(); i--;) {
560 CSearchFile* file = ((CSearchFile*)GetItemData(i));
561 if (file->ShowChildren()) {
562 ShowChildren(file, false);
566 // Then do the sorting
567 CMuleListCtrl::SetSorting(column, order);
568 Thaw();
572 void CSearchListCtrl::SyncLists( CSearchListCtrl* src, CSearchListCtrl* dst )
574 wxCHECK_RET(src && dst, wxT("NULL argument in SyncLists"));
576 // Column widths
577 for ( int i = 0; i < src->GetColumnCount(); i++ ) {
578 // We do this check since just setting the width causes a redraw
579 if ( dst->GetColumnWidth( i ) != src->GetColumnWidth( i ) ) {
580 dst->SetColumnWidth( i, src->GetColumnWidth( i ) );
584 // Sync sorting
585 unsigned column = src->GetSortColumn();
586 unsigned order = src->GetSortOrder();
587 if (column != dst->GetSortColumn() || order != dst->GetSortOrder()) {
588 dst->SetSorting(column, order);
593 void CSearchListCtrl::SyncOtherLists(CSearchListCtrl *src)
595 std::list<CSearchListCtrl*>::iterator it;
597 for (it = s_lists.begin(); it != s_lists.end(); ++it) {
598 if ((*it) != src) {
599 SyncLists( src, *it );
605 void CSearchListCtrl::OnRightClick(wxListEvent& event)
607 CheckSelection(event);
609 if (GetSelectedItemCount()) {
610 // Create the popup-menu
611 wxMenu menu(_("File"));
612 menu.Append(MP_RESUME, _("Download"));
614 wxMenu* cats = new wxMenu(_("Category"));
615 cats->Append(MP_ASSIGNCAT, _("Main"));
616 for (unsigned i = 1; i < theApp->glob_prefs->GetCatCount(); i++) {
617 cats->Append(MP_ASSIGNCAT + i,
618 theApp->glob_prefs->GetCategory(i)->title);
621 menu.Append(MP_MENU_CATS, _("Download in category"), cats);
622 menu.AppendSeparator();
624 const wxString & statsServer = thePrefs::GetStatsServerName();
625 if (!statsServer.IsEmpty()) {
626 menu.Append(MP_RAZORSTATS, CFormat(_("Get %s for this file")) % statsServer);
627 menu.AppendSeparator();
630 menu.Append(MP_SEARCHRELATED, _("Search related files (eD2k, local server)"));
631 menu.AppendSeparator();
633 //#warning Uncomment this here to test the MP_MARK_AS_KNOWN feature. Beware! You are on your own here, this might break "known.met"
634 #if 0
635 menu.Append(MP_MARK_AS_KNOWN, _("Mark as known file"));
636 menu.AppendSeparator();
637 #endif
639 menu.Append(MP_GETED2KLINK, _("Copy eD2k link to clipboard"));
641 // These should only be enabled for single-selections
642 bool enable = (GetSelectedItemCount() == 1);
643 menu.Enable(MP_GETED2KLINK, enable);
644 menu.Enable(MP_MENU_CATS, (theApp->glob_prefs->GetCatCount() > 1));
646 PopupMenu(&menu, event.GetPoint());
647 } else {
648 event.Skip();
653 void CSearchListCtrl::OnColumnLClick( wxListEvent& event )
655 // Let the real event handler do its work first
656 CMuleListCtrl::OnColumnLClick( event );
658 SyncOtherLists( this );
662 void CSearchListCtrl::OnColumnResize( wxListEvent& WXUNUSED(event) )
664 SyncOtherLists( this );
668 void CSearchListCtrl::OnPopupGetUrl( wxCommandEvent& WXUNUSED(event) )
670 wxString URIs;
672 long index = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
674 while (index != -1) {
675 CSearchFile* file = (CSearchFile*)GetItemData( index );
677 URIs += theApp->CreateED2kLink( file ) + wxT("\n");
679 index = GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
682 if (!URIs.IsEmpty()) {
683 theApp->CopyTextToClipboard( URIs.RemoveLast() );
688 void CSearchListCtrl::OnRazorStatsCheck( wxCommandEvent& WXUNUSED(event) )
690 int item = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
691 if (item == -1) {
692 return;
695 CSearchFile* file = (CSearchFile*)GetItemData( item );
696 theApp->amuledlg->LaunchUrl(thePrefs::GetStatsServerURL() + file->GetFileHash().Encode());
700 void CSearchListCtrl::OnRelatedSearch( wxCommandEvent& WXUNUSED(event) )
702 int item = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
703 if (item == -1) {
704 return;
707 CSearchFile* file = (CSearchFile*)GetItemData( item );
708 theApp->searchlist->StopSearch(true);
709 theApp->amuledlg->m_searchwnd->ResetControls();
710 CastByID( IDC_SEARCHNAME, theApp->amuledlg->m_searchwnd, wxTextCtrl )->
711 SetValue(wxT("related::") + file->GetFileHash().Encode());
712 theApp->amuledlg->m_searchwnd->StartNewSearch();
716 void CSearchListCtrl::OnMarkAsKnown( wxCommandEvent& WXUNUSED(event) )
718 #ifndef CLIENT_GUI
719 long index = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
720 while (index > -1) {
721 CSearchFile *searchFile = (CSearchFile *)GetItemData(index);
722 CKnownFile *knownFile(new CKnownFile(*searchFile));
723 theApp->knownfiles->SafeAddKFile(knownFile);
724 index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
726 #endif
730 void CSearchListCtrl::OnPopupDownload(wxCommandEvent& event)
732 if (event.GetId() == MP_RESUME) {
733 // Via the "Download" menu-item, use category specified in drop-down menu
734 DownloadSelected();
735 } else {
736 // Via an "Download in category" item
737 DownloadSelected(event.GetId() - MP_ASSIGNCAT);
742 void CSearchListCtrl::OnItemActivated(wxListEvent& event)
744 CSearchFile* file = ((CSearchFile*)GetItemData(event.GetIndex()));
745 if (file->HasChildren()) {
746 ShowChildren(file, !file->ShowChildren());
747 } else {
748 DownloadSelected();
753 bool CSearchListCtrl::AltSortAllowed(unsigned column) const
755 switch (column) {
756 case ID_SEARCH_COL_SOURCES:
757 return true;
758 default:
759 return false;
764 void CSearchListCtrl::DownloadSelected(int category)
766 FindWindowById(IDC_SDOWNLOAD)->Enable(FALSE);
768 // Either the "Download" menu-item, the download-button, double-click or enter
769 if (category == -1) {
770 // Defaults to main category
771 category = 0;
773 if (CastByID(IDC_EXTENDEDSEARCHCHECK, NULL, wxCheckBox)->GetValue()) {
774 category = CastByID(ID_AUTOCATASSIGN, NULL, wxChoice)->GetSelection();
778 // Process all selections
779 long index = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
780 while (index > -1) {
781 CSearchFile* file = (CSearchFile*)GetItemData(index);
782 CoreNotify_Search_Add_Download(file, category);
783 index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
785 // Listcontrol gets updated by notification when download is started
789 const wxBrush& GetBrush(wxSystemColour index)
791 return CMuleColour(index).GetBrush();
795 void CSearchListCtrl::OnDrawItem(
796 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
798 CSearchFile* file = (CSearchFile*)GetItemData(item);
800 // Define text-color and background
801 if (highlighted) {
802 if (GetFocus()) {
803 dc->SetBackground(GetBrush(wxSYS_COLOUR_HIGHLIGHT));
804 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
805 } else {
806 dc->SetBackground(GetBrush(wxSYS_COLOUR_BTNSHADOW));
807 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
809 } else {
810 dc->SetBackground(GetBrush(wxSYS_COLOUR_LISTBOX));
811 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_WINDOWTEXT));
814 // Define the border of the drawn area
815 if (highlighted) {
816 dc->SetPen(*(wxThePenList->FindOrCreatePen(CMuleColour(dc->GetBackground().GetColour()).Blend(65), 1, wxSOLID)));
817 } else {
818 dc->SetPen(*wxTRANSPARENT_PEN);
819 dc->SetTextForeground(GetItemTextColour(item));
822 // Clear the background, not done automatically since the drawing is buffered.
823 dc->SetBrush( dc->GetBackground() );
824 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
826 // Various constant values we use
827 const int iTextOffset = ( rect.GetHeight() - dc->GetCharHeight() ) / 2;
828 const int iOffset = 4;
829 const int treeOffset = 11;
830 const int treeCenter = 6;
831 bool tree_show = false;
833 wxRect cur_rec(iOffset, rect.y, 0, rect.height );
834 for (int i = 0; i < GetColumnCount(); i++) {
835 wxListItem listitem;
836 GetColumn(i, listitem);
838 if ( listitem.GetWidth() > 0 ) {
839 cur_rec.width = listitem.GetWidth() - 2*iOffset;
841 // Make a copy of the current rectangle so we can apply specific tweaks
842 wxRect target_rec = cur_rec;
844 // will ensure that text is about in the middle ;)
845 target_rec.y += iTextOffset;
847 if (i == 0) {
848 if (file->HasChildren() || file->GetParent()) {
849 tree_show = (listitem.GetWidth() > 0);
850 target_rec.x += treeOffset;
851 target_rec.width -= treeOffset;
853 // Children are indented a bit
854 if (file->GetParent()) {
855 target_rec.x += 4;
856 target_rec.width -= 4;
860 // Check if the rating icon should be drawn
861 if (file->HasRating()) {
862 int image = Client_InvalidRating_Smiley + file->UserRating() - 1;
864 int imgWidth = 16;
866 theApp->amuledlg->m_imagelist.Draw(image, *dc, target_rec.GetX(),
867 target_rec.GetY() - 1, wxIMAGELIST_DRAW_TRANSPARENT);
869 // Move the text past the icon.
870 target_rec.x += imgWidth + 4;
871 target_rec.width -= imgWidth + 4;
875 wxListItem cellitem;
876 cellitem.SetColumn(i);
877 cellitem.SetId(item);
879 // Force clipper (clip 2 px more than the rectangle from the right side)
880 wxDCClipper clipper(*dc, target_rec.x, target_rec.y, target_rec.width - 2, target_rec.height);
882 if (GetItem(cellitem)) {
883 dc->DrawText(cellitem.GetText(), target_rec.GetX(), target_rec.GetY());
884 } else {
885 dc->DrawText(wxT("GetItem failed!"), target_rec.GetX(), target_rec.GetY());
888 // Increment to the next column
889 cur_rec.x += listitem.GetWidth();
893 // Draw tree last so it draws over selected and focus (looks better)
894 if (tree_show) {
895 // Gather some information
896 const bool notLast = (item + 1 < GetItemCount());
897 const bool notFirst = (item != 0);
898 const bool hasNext = notLast && ((CSearchFile*)GetItemData(item + 1))->GetParent();
899 const int middle = cur_rec.y + ( cur_rec.height + 1 ) / 2;
901 // Set up a new pen for drawing the tree
902 dc->SetPen( *(wxThePenList->FindOrCreatePen(dc->GetTextForeground(), 1, wxSOLID)) );
904 if (file->GetParent()) {
905 // Draw the line to the filename
906 dc->DrawLine(treeCenter, middle, treeOffset + 4, middle);
908 // Draw the line to the child node
909 if (hasNext) {
910 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y + cur_rec.height + 1);
913 // Draw the line back up to parent node
914 if (notFirst) {
915 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y - 1);
917 } else if (file->HasChildren()) {
918 if (file->ShowChildren()) {
919 // Draw empty circle
920 dc->SetBrush(*wxTRANSPARENT_BRUSH);
921 } else {
922 dc->SetBrush(*(wxTheBrushList->FindOrCreateBrush(GetItemTextColour(item))));
925 dc->DrawCircle( treeCenter, middle, 3 );
927 // Draw the line to the child node if there are any children
928 if (hasNext && file->ShowChildren()) {
929 dc->DrawLine(treeCenter, middle + 3, treeCenter, cur_rec.y + cur_rec.height + 1);
934 // Sanity checks to ensure that results/children are properly positioned.
935 #ifdef __WXDEBUG__
937 CSearchFile* parent = file->GetParent();
939 if (item > 0) {
940 CSearchFile* before = (CSearchFile*)GetItemData(item - 1);
941 wxASSERT(before);
942 if (parent) {
943 wxASSERT((before->GetParent() == parent) || (before == parent));
944 } else {
945 wxASSERT(before->GetParent() != file);
949 if (item < GetItemCount() - 1) {
950 CSearchFile* after = (CSearchFile*)GetItemData(item + 1);
951 wxASSERT(after);
952 if (parent) {
953 wxASSERT((after->GetParent() == parent) || (!after->GetParent()));
954 } else {
955 wxASSERT((after->GetParent() == file) || (!after->GetParent()));
959 #endif
963 void CSearchListCtrl::ShowChildren(CSearchFile* file, bool show)
965 Freeze();
967 file->SetShowChildren(show);
969 const CSearchResultList& results = file->GetChildren();
970 for (size_t i = 0; i < results.size(); ++i) {
971 if (show) {
972 AddResult(results[i]);
973 } else {
974 RemoveResult(results[i]);
978 Thaw();
982 wxString CSearchListCtrl::GetTTSText(unsigned item) const
984 return GetItemText(item);
988 wxString CSearchListCtrl::DetermineStatusPrintable(CSearchFile *toshow)
990 switch (toshow->GetDownloadStatus()) {
991 case CSearchFile::DOWNLOADED:
992 // File has already been downloaded.
993 return _("Downloaded");
994 case CSearchFile::QUEUED:
995 // File is downloading.
996 case CSearchFile::QUEUEDCANCELED:
997 // File is downloading and has been canceled before.
998 return _("Queued");
999 case CSearchFile::CANCELED:
1000 // File has been canceled.
1001 return _("Canceled");
1002 default:
1003 // File is new.
1004 return _("New");
1007 // File_checked_for_headers