Upstream tarball 9574
[amule.git] / src / SearchListCtrl.cpp
blobb3e7aa6477c1704dd0d6bae61d787692c0cfaf1a
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 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 "DownloadQueue.h" // Needed for CDownloadQueue
32 #include "KnownFileList.h" // Needed for CKnownFileList
33 #include "PartFile.h" // Needed for CPartFile and CKnownFile
34 #include "SearchList.h" // Needed for CSearchFile
35 #include "SearchDlg.h" // Needed for CSearchDlg
36 #include "amuleDlg.h" // Needed for CamuleDlg
37 #include "muuli_wdr.h" // Needed for clientImages
38 #include "Preferences.h" // Needed for thePrefs
39 #include "GuiEvents.h" // Needed for CoreNotify_Search_Add_Download
40 #include "MuleColour.h"
42 BEGIN_EVENT_TABLE(CSearchListCtrl, CMuleListCtrl)
43 EVT_LIST_ITEM_RIGHT_CLICK(-1, CSearchListCtrl::OnRightClick)
44 EVT_LIST_COL_CLICK( -1, CSearchListCtrl::OnColumnLClick)
45 EVT_LIST_COL_END_DRAG( -1, CSearchListCtrl::OnColumnResize)
47 EVT_MENU( MP_GETED2KLINK, CSearchListCtrl::OnPopupGetUrl)
48 EVT_MENU( MP_RAZORSTATS, CSearchListCtrl::OnRazorStatsCheck)
49 EVT_MENU( MP_SEARCHRELATED, CSearchListCtrl::OnRelatedSearch)
50 EVT_MENU( MP_MARK_AS_KNOWN, CSearchListCtrl::OnMarkAsKnown)
51 EVT_MENU( MP_RESUME, CSearchListCtrl::OnPopupDownload)
52 EVT_MENU_RANGE( MP_ASSIGNCAT, MP_ASSIGNCAT + 99, CSearchListCtrl::OnPopupDownload )
54 EVT_LIST_ITEM_ACTIVATED( -1, CSearchListCtrl::OnItemActivated)
55 END_EVENT_TABLE()
58 std::list<CSearchListCtrl*> CSearchListCtrl::s_lists;
61 enum SearchListColumns {
62 ID_SEARCH_COL_NAME = 0,
63 ID_SEARCH_COL_SIZE,
64 ID_SEARCH_COL_SOURCES,
65 ID_SEARCH_COL_TYPE,
66 ID_SEARCH_COL_FILEID,
67 ID_SEARCH_COL_STATUS
71 CSearchListCtrl::CSearchListCtrl(
72 wxWindow *parent,
73 wxWindowID winid,
74 const wxPoint &pos,
75 const wxSize &size,
76 long style,
77 const wxValidator &validator,
78 const wxString &name)
80 CMuleListCtrl(parent, winid, pos, size, style | wxLC_OWNERDRAW, validator, name),
81 m_filterKnown(false),
82 m_invert(false),
83 m_filterEnabled(false)
85 // Setting the sorter function.
86 SetSortFunc( SortProc );
88 InsertColumn( ID_SEARCH_COL_NAME, _("File Name"), wxLIST_FORMAT_LEFT, 500, wxT("N") );
89 InsertColumn( ID_SEARCH_COL_SIZE, _("Size"), wxLIST_FORMAT_LEFT, 100, wxT("Z") );
90 InsertColumn( ID_SEARCH_COL_SOURCES, _("Sources"), wxLIST_FORMAT_LEFT, 50, wxT("u") );
91 InsertColumn( ID_SEARCH_COL_TYPE, _("Type"), wxLIST_FORMAT_LEFT, 65, wxT("Y") );
92 InsertColumn( ID_SEARCH_COL_FILEID, _("FileID"), wxLIST_FORMAT_LEFT, 280, wxT("I") );
93 InsertColumn( ID_SEARCH_COL_STATUS, _("Status"), wxLIST_FORMAT_LEFT, 100, wxT("S") );
95 m_nResultsID = 0;
97 // Only load settings for first list, otherwise sync with current lists
98 if ( s_lists.empty() ) {
99 // Set the name to enable loading of settings
100 SetTableName( wxT("Search") );
102 LoadSettings();
104 // Unset the name to avoid the settings getting saved every time a list is closed
105 SetTableName( wxEmptyString );
106 } else {
107 // Sync this list with one of the others
108 SyncLists( s_lists.front(), this );
111 // Add the list so that it will be synced with the other lists
112 s_lists.push_back( this );
116 wxString CSearchListCtrl::GetOldColumnOrder() const
118 return wxT("N,Z,u,Y,I,S");
122 CSearchListCtrl::~CSearchListCtrl()
124 std::list<CSearchListCtrl*>::iterator it = std::find( s_lists.begin(), s_lists.end(), this );
126 if ( it != s_lists.end() )
127 s_lists.erase( it );
129 // We only save the settings if the last list was closed
130 if ( s_lists.empty() ) {
131 // In order to get the settings saved, we need to set the name
132 SetTableName( wxT("Search") );
137 void CSearchListCtrl::AddResult(CSearchFile* toshow)
139 wxCHECK_RET(toshow->GetSearchID() == m_nResultsID, wxT("Wrong search-id for result-list"));
141 const wxUIntPtr toshowdata = reinterpret_cast<wxUIntPtr>(toshow);
143 // Check if the result should be shown
144 if (FindItem(-1, toshowdata) != -1) {
145 return;
146 } else if (toshow->GetParent() && !toshow->GetParent()->ShowChildren()) {
147 return;
148 } else if (!IsFiltered(toshow)) {
149 if (toshow->HasChildren() && toshow->ShowChildren()) {
150 // Only filter the parent if none of the children are shown.
151 bool foundChild = false;
152 const CSearchResultList& children = toshow->GetChildren();
153 for (size_t i = 0; i < children.size(); ++i) {
154 if (IsFiltered(children.at(i))) {
155 foundChild = true;
156 break;
160 if (!foundChild) {
161 // No children left, and the parent is filtered.
162 m_filteredOut.push_back(toshow);
163 return;
165 } else {
166 m_filteredOut.push_back(toshow);
167 return;
171 // Insert the item before the item found by the search
172 uint32 newid = InsertItem(GetInsertPos(toshowdata), toshow->GetFileName().GetPrintable());
174 // Sanity checks to ensure that results/children are properly positioned.
175 #ifdef __WXDEBUG__
177 CSearchFile* parent = toshow->GetParent();
179 if (newid > 0) {
180 CSearchFile* before = (CSearchFile*)GetItemData(newid - 1);
181 wxASSERT(before);
182 if (parent) {
183 wxASSERT((before->GetParent() == parent) || (before == parent));
184 } else {
185 wxASSERT(before->GetParent() != toshow);
189 if ((int)newid < GetItemCount() - 1) {
190 CSearchFile* after = (CSearchFile*)GetItemData(newid + 1);
191 wxASSERT(after);
192 if (parent) {
193 wxASSERT((after->GetParent() == parent) || (!after->GetParent()));
194 } else {
195 wxASSERT((after->GetParent() == toshow) || (!after->GetParent()));
199 #endif
201 SetItemPtrData(newid, toshowdata);
203 // Filesize
204 SetItem(newid, ID_SEARCH_COL_SIZE, CastItoXBytes( toshow->GetFileSize() ) );
206 // Source count
207 wxString temp = wxString::Format(wxT("%d"), toshow->GetSourceCount());
208 if (toshow->GetCompleteSourceCount()) {
209 temp += wxString::Format(wxT(" (%d)"), toshow->GetCompleteSourceCount());
211 if (toshow->GetClientsCount()) {
212 temp += wxString::Format(wxT(" [%d]"), toshow->GetClientsCount());
214 #ifdef __DEBUG__
215 if (toshow->GetKadPublishInfo() == 0) {
216 temp += wxT(" | -");
217 } else {
218 temp += wxString::Format(wxT(" | N:%u, P:%u, T:%0.2f"), (toshow->GetKadPublishInfo() & 0xFF000000) >> 24, (toshow->GetKadPublishInfo() & 0x00FF0000) >> 16, (toshow->GetKadPublishInfo() & 0x0000FFFF) / 100.0);
220 #endif
221 SetItem( newid, ID_SEARCH_COL_SOURCES, temp );
223 // File-type
224 SetItem( newid, ID_SEARCH_COL_TYPE, GetFiletypeByName( toshow->GetFileName() ) );
226 // File-hash
227 SetItem(newid, ID_SEARCH_COL_FILEID, toshow->GetFileHash().Encode() );
229 // File status
230 SetItem(newid, ID_SEARCH_COL_STATUS, DetermineStatusPrintable(toshow));
232 // Set the color of the item
233 UpdateItemColor( newid );
237 void CSearchListCtrl::RemoveResult(CSearchFile* toremove)
239 ShowChildren(toremove, false);
241 long index = FindItem(-1, reinterpret_cast<wxUIntPtr>(toremove));
242 if (index != -1) {
243 DeleteItem(index);
244 } else {
245 ResultList::iterator it = std::find(m_filteredOut.begin(), m_filteredOut.end(), toremove);
246 if ( it != m_filteredOut.end()) {
247 m_filteredOut.erase(it);
253 void CSearchListCtrl::UpdateResult(CSearchFile* toupdate)
255 long index = FindItem(-1, reinterpret_cast<wxUIntPtr>(toupdate));
256 if (index != -1) {
257 // Update the filename, which may be changed in case of multiple variants.
258 SetItem(index, ID_SEARCH_COL_NAME, toupdate->GetFileName().GetPrintable());
260 wxString temp = wxString::Format(wxT("%d"), toupdate->GetSourceCount());
261 if (toupdate->GetCompleteSourceCount()) {
262 temp += wxString::Format(wxT(" (%d)"), toupdate->GetCompleteSourceCount());
264 if (toupdate->GetClientsCount()) {
265 temp += wxString::Format(wxT(" [%d]"), toupdate->GetClientsCount());
267 #ifdef __DEBUG__
268 if (toupdate->GetKadPublishInfo() == 0) {
269 temp += wxT(" | -");
270 } else {
271 temp += wxString::Format(wxT(" | N:%u, P:%u, T:%0.2f"), (toupdate->GetKadPublishInfo() & 0xFF000000) >> 24, (toupdate->GetKadPublishInfo() & 0x00FF0000) >> 16, (toupdate->GetKadPublishInfo() & 0x0000FFFF) / 100.0);
273 #endif
274 SetItem(index, ID_SEARCH_COL_SOURCES, temp);
276 SetItem(index, ID_SEARCH_COL_STATUS, DetermineStatusPrintable(toupdate));
278 UpdateItemColor(index);
280 // Deletions of items causes rather large amount of flicker, so to
281 // avoid this, we resort the list to ensure correct ordering.
282 if (!IsItemSorted(index)) {
283 SortList();
289 void CSearchListCtrl::UpdateItemColor( long index )
291 wxListItem item;
292 item.SetId( index );
293 item.SetColumn( ID_SEARCH_COL_SIZE );
294 item.SetMask( wxLIST_MASK_STATE|wxLIST_MASK_TEXT|wxLIST_MASK_IMAGE|wxLIST_MASK_DATA|wxLIST_MASK_WIDTH|wxLIST_MASK_FORMAT );
296 if ( GetItem(item) ) {
297 CMuleColour newcol(wxSYS_COLOUR_WINDOWTEXT);
299 CSearchFile* file = (CSearchFile*)GetItemData(index);
300 CKnownFile* sameFile = theApp->downloadqueue->GetFileByID(file->GetFileHash());
301 if ( !sameFile ) {
302 sameFile = theApp->knownfiles->FindKnownFileByID(file->GetFileHash());
305 int red = newcol.Red();
306 int green = newcol.Green();
307 int blue = newcol.Blue();
309 if ( sameFile ) {
310 if ( sameFile->IsPartFile() ) {
311 // File is already being downloaded. Mark as red.
312 red = 255;
313 } else if ( sameFile->GetStatus() == PS_COMPLETE ) {
314 // File has already been downloaded. Mark as green.
315 green = 200;
316 } else {
317 // File has been cancelled or removed. Mark as grey.
318 red = 128;
319 green = 128;
320 blue = 128;
322 } else {
323 // File is new, colour after number of files
324 blue += file->GetSourceCount() * 5;
325 if ( blue > 255 ) {
326 blue = 255;
330 // don't forget to set the item data back...
331 wxListItem newitem;
332 newitem.SetId( index );
333 newitem.SetTextColour( wxColour( red, green, blue ) );
334 SetItem( newitem );
339 // Update the colors of all assosiated items, which means parents and/or siblings.
340 void CSearchListCtrl::UpdateAllRelativesColor(
341 CSearchFile *file,
342 long index)
344 if ((file->ShowChildren() && file->HasChildren()) ||
345 file->GetParent()) {
346 CSearchFile *parent = file->GetParent() ?
347 file->GetParent() : file;
348 const CSearchResultList &list = parent->GetChildren();
349 for (size_t j = 0; j < list.size(); ++j) {
350 UpdateItemColor(FindItem(-1, reinterpret_cast<wxUIntPtr>(list.at(j))));
352 UpdateItemColor(FindItem(-1, reinterpret_cast<wxUIntPtr>(parent)));
353 } else {
354 UpdateItemColor(index);
359 void CSearchListCtrl::ShowResults( long ResultsID )
361 DeleteAllItems();
362 m_nResultsID = ResultsID;
363 if (ResultsID) {
364 const CSearchResultList& list = theApp->searchlist->GetSearchResults(ResultsID);
365 for (unsigned int i = 0; i < list.size(); ++i) {
366 AddResult( list[i] );
372 wxUIntPtr CSearchListCtrl::GetSearchId()
374 return m_nResultsID;
378 void CSearchListCtrl::SetFilter(const wxString& regExp, bool invert, bool filterKnown)
380 if (regExp.IsEmpty()) {
381 // Show everything
382 m_filterText = wxT(".*");
383 } else {
384 m_filterText = regExp;
387 m_filter.Compile(m_filterText, wxRE_DEFAULT | wxRE_ICASE);
388 m_filterKnown = filterKnown;
389 m_invert = invert;
391 if (m_filterEnabled) {
392 // Swap the list of filtered results so we can freely add new items to the list
393 ResultList curFiltered;
394 std::swap(curFiltered, m_filteredOut);
396 // Filter items already on the list
397 for (int i = 0; i < GetItemCount();) {
398 CSearchFile* file = (CSearchFile*)GetItemData(i);
400 if (IsFiltered(file)) {
401 ++i;
402 } else {
403 m_filteredOut.push_back(file);
404 DeleteItem(i);
408 // Check the previously filtered items.
409 ResultList::iterator it = curFiltered.begin();
410 for (; it != curFiltered.end(); ++it) {
411 if (IsFiltered(*it)) {
412 AddResult(*it);
413 } else {
414 m_filteredOut.push_back(*it);
421 void CSearchListCtrl::EnableFiltering(bool enabled)
423 if (enabled != m_filterEnabled) {
424 m_filterEnabled = enabled;
426 if (enabled) {
427 SetFilter(m_filterText, m_invert, m_filterKnown);
428 } else {
429 ResultList::iterator it = m_filteredOut.begin();
430 for (; it != m_filteredOut.end(); ++it) {
431 AddResult(*it);
434 m_filteredOut.clear();
440 size_t CSearchListCtrl::GetHiddenItemCount() const
442 return m_filteredOut.size();
446 bool CSearchListCtrl::IsFiltered(const CSearchFile* file)
448 // By default, everything is displayed
449 bool result = true;
451 if (m_filterEnabled && m_filter.IsValid()) {
452 result = m_filter.Matches(file->GetFileName().GetPrintable());
453 result = ((result && !m_invert) || (!result && m_invert));
455 if (result && m_filterKnown) {
456 result = !theApp->downloadqueue->GetFileByID(file->GetFileHash());
458 if (result) {
459 result = !theApp->knownfiles->FindKnownFileByID(file->GetFileHash());
464 return result;
468 int CSearchListCtrl::SortProc(wxUIntPtr item1, wxUIntPtr item2, long sortData)
470 CSearchFile* file1 = (CSearchFile*)item1;
471 CSearchFile* file2 = (CSearchFile*)item2;
473 // Modifies the result, 1 for ascending, -1 for decending
474 int modifier = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
475 bool alternate = (sortData & CMuleListCtrl::SORT_ALT) != 0;
477 // Decide if which should files we should sort by.
478 wxUIntPtr parent1 = reinterpret_cast<wxUIntPtr>(file1->GetParent());
479 wxUIntPtr parent2 = reinterpret_cast<wxUIntPtr>(file2->GetParent());
480 wxUIntPtr filePtr1 = reinterpret_cast<wxUIntPtr>(file1);
481 wxUIntPtr filePtr2 = reinterpret_cast<wxUIntPtr>(file2);
482 if (parent1 && parent2) {
483 if (parent1 != parent2) {
484 return SortProc(parent1, parent2, sortData);
486 } else if (parent1) {
487 if (parent1 == filePtr2) {
488 return 1;
489 } else {
490 return SortProc(parent1, filePtr2, sortData);
492 } else if (parent2) {
493 if (parent2 == filePtr1) {
494 return -1;
495 } else {
496 return SortProc(filePtr1, parent2, sortData);
500 int result = 0;
501 switch (sortData & CMuleListCtrl::COLUMN_MASK) {
502 // Sort by filename
503 case ID_SEARCH_COL_NAME:
504 result = CmpAny(file1->GetFileName(), file2->GetFileName());
505 break;
507 // Sort file-size
508 case ID_SEARCH_COL_SIZE:
509 result = CmpAny( file1->GetFileSize(), file2->GetFileSize() );
510 break;
512 // Sort by sources
513 case ID_SEARCH_COL_SOURCES: {
514 int cmp = CmpAny( file1->GetSourceCount(), file2->GetSourceCount() );
515 int cmp2 = CmpAny( file1->GetCompleteSourceCount(), file2->GetCompleteSourceCount() );
517 if ( alternate ) {
518 // Swap criterias
519 int temp = cmp2;
520 cmp2 = cmp;
521 cmp = temp;
524 if ( cmp == 0 ) {
525 cmp = cmp2;
528 result = cmp;
529 break;
532 // Sort by file-types
533 case ID_SEARCH_COL_TYPE: {
534 result = GetFiletypeByName(file1->GetFileName()).Cmp(GetFiletypeByName(file2->GetFileName()));
535 if (result == 0) {
536 // Same file-type, sort by extension
537 result = CmpAny(file1->GetFileName().GetExt(), file2->GetFileName().GetExt());
540 break;
543 // Sort by file-hash
544 case ID_SEARCH_COL_FILEID:
545 result = CmpAny(file2->GetFileHash(), file1->GetFileHash());
547 // Sort by file status
548 case ID_SEARCH_COL_STATUS:
549 result = CmpAny(DetermineStatusPrintable(file2), DetermineStatusPrintable(file1));
552 return modifier * result;
556 void CSearchListCtrl::SyncLists( CSearchListCtrl* src, CSearchListCtrl* dst )
558 wxCHECK_RET(src && dst, wxT("NULL argument in SyncLists"));
560 // Column widths
561 for ( int i = 0; i < src->GetColumnCount(); i++ ) {
562 // We do this check since just setting the width causes a redraw
563 if ( dst->GetColumnWidth( i ) != src->GetColumnWidth( i ) ) {
564 dst->SetColumnWidth( i, src->GetColumnWidth( i ) );
568 // Sync sorting
569 unsigned column = src->GetSortColumn();
570 unsigned order = src->GetSortOrder();
571 if (column != dst->GetSortColumn() || order != dst->GetSortOrder()) {
572 dst->SetSorting(column, order);
577 void CSearchListCtrl::SyncOtherLists( CSearchListCtrl* src )
579 std::list<CSearchListCtrl*>::iterator it;
581 for ( it = s_lists.begin(); it != s_lists.end(); ++it ) {
582 if ( (*it) != src ) {
583 SyncLists( src, *it );
589 void CSearchListCtrl::OnRightClick(wxListEvent& event)
591 CheckSelection(event);
593 if (GetSelectedItemCount()) {
594 // Create the popup-menu
595 wxMenu menu(_("File"));
596 menu.Append(MP_RESUME, _("Download"));
598 wxMenu* cats = new wxMenu(_("Category"));
599 cats->Append(MP_ASSIGNCAT, _("Main"));
600 for (unsigned i = 1; i < theApp->glob_prefs->GetCatCount(); i++) {
601 cats->Append(MP_ASSIGNCAT + i,
602 theApp->glob_prefs->GetCategory(i)->title);
605 menu.Append(MP_MENU_CATS, _("Download in category"), cats);
606 menu.AppendSeparator();
607 /* Commented out while it's gone
608 menu.Append(MP_RAZORSTATS, _("Get Razorback 2's stats for this file"));
609 menu.AppendSeparator();
611 menu.Append(MP_SEARCHRELATED, _("Search related files (eD2k, local server)"));
612 menu.AppendSeparator();
614 //#warning Uncomment this here to test the MP_MARK_AS_KNOWN feature. Beware! You are on your own here, this might break "known.met"
615 #if 0
616 menu.Append(MP_MARK_AS_KNOWN, _("Mark as known file"));
617 menu.AppendSeparator();
618 #endif
620 menu.Append(MP_GETED2KLINK, _("Copy eD2k link to clipboard"));
622 // These should only be enabled for single-selections
623 bool enable = (GetSelectedItemCount() == 1);
624 menu.Enable(MP_GETED2KLINK, enable);
625 menu.Enable(MP_MENU_CATS, (theApp->glob_prefs->GetCatCount() > 1));
627 PopupMenu(&menu, event.GetPoint());
628 } else {
629 event.Skip();
634 void CSearchListCtrl::OnColumnLClick( wxListEvent& event )
636 // Let the real event handler do its work first
637 CMuleListCtrl::OnColumnLClick( event );
639 SyncOtherLists( this );
643 void CSearchListCtrl::OnColumnResize( wxListEvent& WXUNUSED(event) )
645 SyncOtherLists( this );
649 void CSearchListCtrl::OnPopupGetUrl( wxCommandEvent& WXUNUSED(event) )
651 wxString URIs;
653 long index = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
655 while( index != -1 ) {
656 CSearchFile* file = (CSearchFile*)GetItemData( index );
658 URIs += theApp->CreateED2kLink( file ) + wxT("\n");
660 index = GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
663 if ( !URIs.IsEmpty() ) {
664 theApp->CopyTextToClipboard( URIs.RemoveLast() );
669 void CSearchListCtrl::OnRazorStatsCheck( wxCommandEvent& WXUNUSED(event) )
671 int item = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
672 if ( item == -1 ) {
673 return;
676 CSearchFile* file = (CSearchFile*)GetItemData( item );
677 theApp->amuledlg->LaunchUrl(wxT("http://stats.razorback2.com/ed2khistory?ed2k=") + file->GetFileHash().Encode());
681 void CSearchListCtrl::OnRelatedSearch( wxCommandEvent& WXUNUSED(event) )
683 int item = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
684 if ( item == -1 ) {
685 return;
688 CSearchFile* file = (CSearchFile*)GetItemData( item );
689 theApp->searchlist->StopGlobalSearch();
690 theApp->amuledlg->m_searchwnd->ResetControls();
691 CastByID( IDC_SEARCHNAME, theApp->amuledlg->m_searchwnd, wxTextCtrl )->
692 SetValue(wxT("related::") + file->GetFileHash().Encode());
693 theApp->amuledlg->m_searchwnd->StartNewSearch();
697 void CSearchListCtrl::OnMarkAsKnown( wxCommandEvent& WXUNUSED(event) )
699 #ifndef CLIENT_GUI
700 long index = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
701 while (index > -1) {
702 CSearchFile *searchFile = (CSearchFile *)GetItemData(index);
703 CKnownFile *knownFile(new CKnownFile(*searchFile));
704 theApp->knownfiles->SafeAddKFile(knownFile);
705 UpdateAllRelativesColor(searchFile, index);
706 index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
708 #endif
712 void CSearchListCtrl::OnPopupDownload(wxCommandEvent& event)
714 if (event.GetId() == MP_RESUME) {
715 // Via the "Download" menu-item, use category specified in drop-down menu
716 DownloadSelected();
717 } else {
718 // Via an "Download in category" item
719 DownloadSelected(event.GetId() - MP_ASSIGNCAT);
724 void CSearchListCtrl::OnItemActivated(wxListEvent& event)
726 CSearchFile* file = ((CSearchFile*)GetItemData(event.GetIndex()));
727 if (file->HasChildren()) {
728 ShowChildren(file, !file->ShowChildren());
729 } else {
730 DownloadSelected();
735 bool CSearchListCtrl::AltSortAllowed(unsigned column) const
737 switch ( column ) {
738 case ID_SEARCH_COL_SOURCES:
739 return true;
741 default:
742 return false;
747 void CSearchListCtrl::DownloadSelected(int category)
749 FindWindowById(IDC_SDOWNLOAD)->Enable(FALSE);
751 // Either the "Download" menu-item, the download-button, double-click or enter
752 if (category == -1) {
753 // Defaults to main category
754 category = 0;
756 if (CastByID(IDC_EXTENDEDSEARCHCHECK, NULL, wxCheckBox)->GetValue()) {
757 category = CastByID(ID_AUTOCATASSIGN, NULL, wxChoice)->GetSelection();
761 // First process all selections (because they may be invalidated by UpdateResult()
762 // if list is sorted by status)
763 std::list<CSearchFile*> searchFiles;
764 long index = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
765 while (index > -1) {
766 CSearchFile* file = (CSearchFile*)GetItemData(index);
767 searchFiles.push_back(file);
768 CoreNotify_Search_Add_Download(file, category);
769 UpdateAllRelativesColor(file, index);
770 index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
772 // Then update the listcontrol
773 for (std::list<CSearchFile*>::iterator it = searchFiles.begin(); it != searchFiles.end(); ++it) {
774 UpdateResult(*it);
779 const wxBrush& GetBrush(wxSystemColour index)
781 return CMuleColour(index).GetBrush();
785 void CSearchListCtrl::OnDrawItem(
786 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
788 CSearchFile* file = (CSearchFile*)GetItemData(item);
790 // Define text-color and background
791 if (highlighted) {
792 if (GetFocus()) {
793 dc->SetBackground(GetBrush(wxSYS_COLOUR_HIGHLIGHT));
794 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
795 } else {
796 dc->SetBackground(GetBrush(wxSYS_COLOUR_BTNSHADOW));
797 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
799 } else {
800 dc->SetBackground(GetBrush(wxSYS_COLOUR_LISTBOX));
801 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_WINDOWTEXT));
804 // Define the border of the drawn area
805 if ( highlighted ) {
806 dc->SetPen(*(wxThePenList->FindOrCreatePen(CMuleColour(dc->GetBackground().GetColour()).Blend(65), 1, wxSOLID)));
807 } else {
808 dc->SetPen(*wxTRANSPARENT_PEN);
809 dc->SetTextForeground(GetItemTextColour(item));
812 // Clear the background, not done automatically since the drawing is buffered.
813 dc->SetBrush( dc->GetBackground() );
814 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
816 // Various constant values we use
817 const int iTextOffset = ( rect.GetHeight() - dc->GetCharHeight() ) / 2;
818 const int iOffset = 4;
819 const int treeOffset = 11;
820 const int treeCenter = 6;
821 bool tree_show = false;
823 wxRect cur_rec(iOffset, rect.y, 0, rect.height );
824 for (int i = 0; i < GetColumnCount(); i++) {
825 wxListItem listitem;
826 GetColumn(i, listitem);
828 if ( listitem.GetWidth() > 0 ) {
829 cur_rec.width = listitem.GetWidth() - 2*iOffset;
831 // Make a copy of the current rectangle so we can apply specific tweaks
832 wxRect target_rec = cur_rec;
834 // will ensure that text is about in the middle ;)
835 target_rec.y += iTextOffset;
837 if (i == 0) {
838 if (file->HasChildren() || file->GetParent()) {
839 tree_show = (listitem.GetWidth() > 0);
840 target_rec.x += treeOffset;
841 target_rec.width -= treeOffset;
843 // Children are indented a bit
844 if (file->GetParent()) {
845 target_rec.x += 4;
846 target_rec.width -= 4;
850 // Check if the rating icon should be drawn
851 if (file->HasRating()) {
852 int image = Client_InvalidRating_Smiley + file->UserRating() - 1;
854 int imgWidth = 16;
856 theApp->amuledlg->m_imagelist.Draw(image, *dc, target_rec.GetX(),
857 target_rec.GetY() - 1, wxIMAGELIST_DRAW_TRANSPARENT);
859 // Move the text past the icon.
860 target_rec.x += imgWidth + 4;
861 target_rec.width -= imgWidth + 4;
865 wxListItem cellitem;
866 cellitem.SetColumn(i);
867 cellitem.SetId(item);
869 // Force clipper (clip 2 px more than the rectangle from the right side)
870 wxDCClipper clipper(*dc, target_rec.x, target_rec.y, target_rec.width - 2, target_rec.height);
872 if (GetItem(cellitem)) {
873 dc->DrawText(cellitem.GetText(), target_rec.GetX(), target_rec.GetY());
874 } else {
875 dc->DrawText(wxT("GetItem failed!"), target_rec.GetX(), target_rec.GetY());
878 // Increment to the next column
879 cur_rec.x += listitem.GetWidth();
883 // Draw tree last so it draws over selected and focus (looks better)
884 if ( tree_show ) {
885 // Gather some information
886 const bool notLast = (item + 1 < GetItemCount());
887 const bool notFirst = (item != 0);
888 const bool hasNext = notLast && ((CSearchFile*)GetItemData(item + 1))->GetParent();
889 const int middle = cur_rec.y + ( cur_rec.height + 1 ) / 2;
891 // Set up a new pen for drawing the tree
892 dc->SetPen( *(wxThePenList->FindOrCreatePen(dc->GetTextForeground(), 1, wxSOLID)) );
894 if (file->GetParent()) {
895 // Draw the line to the filename
896 dc->DrawLine(treeCenter, middle, treeOffset + 4, middle);
898 // Draw the line to the child node
899 if (hasNext) {
900 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y + cur_rec.height + 1);
903 // Draw the line back up to parent node
904 if (notFirst) {
905 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y - 1);
907 } else if (file->HasChildren()) {
908 if (file->ShowChildren()) {
909 // Draw empty circle
910 dc->SetBrush(*wxTRANSPARENT_BRUSH);
911 } else {
912 dc->SetBrush(*(wxTheBrushList->FindOrCreateBrush(GetItemTextColour(item))));
915 dc->DrawCircle( treeCenter, middle, 3 );
917 // Draw the line to the child node if there are any children
918 if (hasNext && file->ShowChildren()) {
919 dc->DrawLine(treeCenter, middle + 3, treeCenter, cur_rec.y + cur_rec.height + 1);
924 // Sanity checks to ensure that results/children are properly positioned.
925 #ifdef __WXDEBUG__
927 CSearchFile* parent = file->GetParent();
929 if (item > 0) {
930 CSearchFile* before = (CSearchFile*)GetItemData(item - 1);
931 wxASSERT(before);
932 if (parent) {
933 wxASSERT((before->GetParent() == parent) || (before == parent));
934 } else {
935 wxASSERT(before->GetParent() != file);
939 if (item < GetItemCount() - 1) {
940 CSearchFile* after = (CSearchFile*)GetItemData(item + 1);
941 wxASSERT(after);
942 if (parent) {
943 wxASSERT((after->GetParent() == parent) || (!after->GetParent()));
944 } else {
945 wxASSERT((after->GetParent() == file) || (!after->GetParent()));
949 #endif
953 void CSearchListCtrl::ShowChildren(CSearchFile* file, bool show)
955 Freeze();
957 file->SetShowChildren(show);
959 const CSearchResultList& results = file->GetChildren();
960 for (size_t i = 0; i < results.size(); ++i) {
961 if (show) {
962 AddResult(results[i]);
963 } else {
964 RemoveResult(results[i]);
968 Thaw();
972 wxString CSearchListCtrl::GetTTSText(unsigned item) const
974 return GetItemText(item);
978 wxString CSearchListCtrl::DetermineStatusPrintable(CSearchFile *toshow)
980 wxString retVal;
982 CKnownFile* sameFile = theApp->downloadqueue->GetFileByID(toshow->GetFileHash());
983 if ( !sameFile ) {
984 sameFile = theApp->knownfiles->FindKnownFileByID(toshow->GetFileHash());
987 if ( sameFile ) {
988 if ( sameFile->IsPartFile() ) {
989 // File is already being downloaded.
990 retVal = wxT("Queued");
991 } else if ( sameFile->GetStatus() == PS_COMPLETE ) {
992 // File has already been downloaded.
993 retVal = wxT("Downloaded");
994 } else {
995 // File has been cancelled or removed.
996 retVal = wxT("Cancelled");
998 } else {
999 // File is new
1000 retVal = wxT("New");
1002 return retVal;
1004 // File_checked_for_headers