No reason to check for Cryptopp < 5.5
[amule.git] / src / SearchListCtrl.cpp
blob58108379f8c94b2f9d4333927206b602902ed45b
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.
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,
66 ID_SEARCH_COL_DIRECTORY
70 CSearchListCtrl::CSearchListCtrl(
71 wxWindow *parent,
72 wxWindowID winid,
73 const wxPoint &pos,
74 const wxSize &size,
75 long style,
76 const wxValidator &validator,
77 const wxString &name)
79 CMuleListCtrl(parent, winid, pos, size, style | wxLC_OWNERDRAW, validator, name),
80 m_filterKnown(false),
81 m_invert(false),
82 m_filterEnabled(false)
84 // Setting the sorter function.
85 SetSortFunc( SortProc );
87 InsertColumn( ID_SEARCH_COL_NAME, _("File Name"), wxLIST_FORMAT_LEFT, 500, wxT("N") );
88 InsertColumn( ID_SEARCH_COL_SIZE, _("Size"), wxLIST_FORMAT_LEFT, 100, wxT("Z") );
89 InsertColumn( ID_SEARCH_COL_SOURCES, _("Sources"), wxLIST_FORMAT_LEFT, 50, wxT("u") );
90 InsertColumn( ID_SEARCH_COL_TYPE, _("Type"), wxLIST_FORMAT_LEFT, 65, wxT("Y") );
91 InsertColumn( ID_SEARCH_COL_FILEID, _("FileID"), wxLIST_FORMAT_LEFT, 280, wxT("I") );
92 InsertColumn( ID_SEARCH_COL_STATUS, _("Status"), wxLIST_FORMAT_LEFT, 100, wxT("S") );
93 InsertColumn( ID_SEARCH_COL_DIRECTORY, _("Directories"), wxLIST_FORMAT_LEFT, 280, wxT("D") ); // I would have prefered "Directory" but this is already translated
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);
142 CSearchFile* parent = toshow->GetParent();
144 // Check if the result should be shown
145 if (FindItem(-1, toshowdata) != -1) {
146 return;
147 } else if (parent && !parent->ShowChildren()) {
148 return;
149 } else if (!IsFiltered(toshow)) {
150 if (toshow->HasChildren() && toshow->ShowChildren()) {
151 // Only filter the parent if none of the children are shown.
152 bool foundChild = false;
153 const CSearchResultList& children = toshow->GetChildren();
154 for (size_t i = 0; i < children.size(); ++i) {
155 if (IsFiltered(children.at(i))) {
156 foundChild = true;
157 break;
161 if (!foundChild) {
162 // No children left, and the parent is filtered.
163 m_filteredOut.push_back(toshow);
164 return;
166 } else {
167 m_filteredOut.push_back(toshow);
168 return;
172 // Insert the item before the item found by the search
173 long insertPos;
174 if (parent) {
175 insertPos = FindItem(-1, (wxUIntPtr)parent);
176 if (insertPos == -1) {
177 wxFAIL;
178 insertPos = GetItemCount();
179 } else {
180 insertPos++;
182 } else {
183 insertPos = GetInsertPos(toshowdata);
185 long newid = InsertItem(insertPos, toshow->GetFileName().GetPrintable());
187 // Sanity checks to ensure that results/children are properly positioned.
188 #ifdef __WXDEBUG__
190 if (newid > 0) {
191 CSearchFile* before = reinterpret_cast<CSearchFile*>(GetItemData(newid - 1));
192 wxASSERT(before);
193 if (parent) {
194 wxASSERT((before->GetParent() == parent) || (before == parent));
195 } else {
196 wxASSERT(before->GetParent() != toshow);
200 if ((int)newid < GetItemCount() - 1) {
201 CSearchFile* after = reinterpret_cast<CSearchFile*>(GetItemData(newid + 1));
202 wxASSERT(after);
203 if (parent) {
204 wxASSERT((after->GetParent() == parent) || (!after->GetParent()));
205 } else {
206 wxASSERT((after->GetParent() == toshow) || (!after->GetParent()));
210 #endif
212 SetItemPtrData(newid, toshowdata);
214 // Filesize
215 SetItem(newid, ID_SEARCH_COL_SIZE, CastItoXBytes( toshow->GetFileSize() ) );
217 // Source count
218 wxString temp = CFormat(wxT("%d")) % toshow->GetSourceCount();
219 if (toshow->GetCompleteSourceCount()) {
220 temp += CFormat(wxT(" (%d)")) % toshow->GetCompleteSourceCount();
222 if (toshow->GetClientsCount()) {
223 temp += CFormat(wxT(" [%d]")) % toshow->GetClientsCount();
225 #if defined(__DEBUG__) && !defined(CLIENT_GUI)
226 if (toshow->GetKadPublishInfo() == 0) {
227 temp += wxT(" | -");
228 } else {
229 temp += CFormat(wxT(" | N:%u, P:%u, T:%0.2f"))
230 % ((toshow->GetKadPublishInfo() & 0xFF000000) >> 24)
231 % ((toshow->GetKadPublishInfo() & 0x00FF0000) >> 16)
232 % ((toshow->GetKadPublishInfo() & 0x0000FFFF) / 100.0);
234 #endif
235 SetItem( newid, ID_SEARCH_COL_SOURCES, temp );
237 // File-type
238 SetItem( newid, ID_SEARCH_COL_TYPE, GetFiletypeByName( toshow->GetFileName() ) );
240 // File-hash
241 SetItem(newid, ID_SEARCH_COL_FILEID, toshow->GetFileHash().Encode() );
243 // File status
244 SetItem(newid, ID_SEARCH_COL_STATUS, DetermineStatusPrintable(toshow));
246 // Directory where file is located (has a value when search file comes from a "view shared files" request)
247 SetItem(newid, ID_SEARCH_COL_DIRECTORY, toshow->GetDirectory());
249 // Set the color of the item
250 UpdateItemColor( newid );
254 void CSearchListCtrl::RemoveResult(CSearchFile* toremove)
256 ShowChildren(toremove, false);
258 long index = FindItem(-1, reinterpret_cast<wxUIntPtr>(toremove));
259 if (index != -1) {
260 DeleteItem(index);
261 } else {
262 ResultList::iterator it = std::find(m_filteredOut.begin(), m_filteredOut.end(), toremove);
263 if ( it != m_filteredOut.end()) {
264 m_filteredOut.erase(it);
270 void CSearchListCtrl::UpdateResult(CSearchFile* toupdate)
272 long index = FindItem(-1, reinterpret_cast<wxUIntPtr>(toupdate));
273 if (index != -1) {
274 // Update the filename, which may be changed in case of multiple variants.
275 SetItem(index, ID_SEARCH_COL_NAME, toupdate->GetFileName().GetPrintable());
277 wxString temp = CFormat(wxT("%d")) % toupdate->GetSourceCount();
278 if (toupdate->GetCompleteSourceCount()) {
279 temp += CFormat(wxT(" (%d)")) % toupdate->GetCompleteSourceCount();
281 if (toupdate->GetClientsCount()) {
282 temp += CFormat(wxT(" [%d]")) % toupdate->GetClientsCount();
284 #if defined(__DEBUG__) && !defined(CLIENT_GUI)
285 if (toupdate->GetKadPublishInfo() == 0) {
286 temp += wxT(" | -");
287 } else {
288 temp += CFormat(wxT(" | N:%u, P:%u, T:%0.2f"))
289 % ((toupdate->GetKadPublishInfo() & 0xFF000000) >> 24)
290 % ((toupdate->GetKadPublishInfo() & 0x00FF0000) >> 16)
291 % ((toupdate->GetKadPublishInfo() & 0x0000FFFF) / 100.0);
293 #endif
294 SetItem(index, ID_SEARCH_COL_SOURCES, temp);
296 SetItem(index, ID_SEARCH_COL_STATUS, DetermineStatusPrintable(toupdate));
298 UpdateItemColor(index);
300 // Deletions of items causes rather large amount of flicker, so to
301 // avoid this, we resort the list to ensure correct ordering.
302 if (!IsItemSorted(index)) {
303 SortList();
309 void CSearchListCtrl::UpdateItemColor(long index)
311 wxListItem item;
312 item.SetId( index );
313 item.SetColumn( ID_SEARCH_COL_SIZE );
314 item.SetMask(
315 wxLIST_MASK_STATE |
316 wxLIST_MASK_TEXT |
317 wxLIST_MASK_IMAGE |
318 wxLIST_MASK_DATA |
319 wxLIST_MASK_WIDTH |
320 wxLIST_MASK_FORMAT);
322 if (GetItem(item)) {
323 CMuleColour newcol(wxSYS_COLOUR_WINDOWTEXT);
325 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(index));
327 int red = newcol.Red();
328 int green = newcol.Green();
329 int blue = newcol.Blue();
331 switch (file->GetDownloadStatus()) {
332 case CSearchFile::DOWNLOADED:
333 // File has already been downloaded. Mark as green.
334 green = 255;
335 break;
336 case CSearchFile::QUEUED:
337 // File is downloading.
338 case CSearchFile::QUEUEDCANCELED:
339 // File is downloading and has been canceled before.
340 // Mark as red
341 red = 255;
342 break;
343 case CSearchFile::CANCELED:
344 // File has been canceled. Mark as magenta.
345 red = 255;
346 blue = 255;
347 break;
348 default:
349 // File is new, colour after number of files
350 blue += file->GetSourceCount() * 5;
351 if ( blue > 255 ) {
352 blue = 255;
356 // don't forget to set the item data back...
357 wxListItem newitem;
358 newitem.SetId( index );
359 newitem.SetTextColour( wxColour( red, green, blue ) );
360 SetItem( newitem );
365 void CSearchListCtrl::ShowResults( long ResultsID )
367 DeleteAllItems();
368 m_nResultsID = ResultsID;
369 if (ResultsID) {
370 const CSearchResultList& list = theApp->searchlist->GetSearchResults(ResultsID);
371 for (unsigned int i = 0; i < list.size(); ++i) {
372 AddResult( list[i] );
378 wxUIntPtr CSearchListCtrl::GetSearchId()
380 return m_nResultsID;
384 void CSearchListCtrl::SetFilter(const wxString& regExp, bool invert, bool filterKnown)
386 if (regExp.IsEmpty()) {
387 // Show everything
388 m_filterText = wxT(".*");
389 } else {
390 m_filterText = regExp;
393 m_filter.Compile(m_filterText, wxRE_DEFAULT | wxRE_ICASE);
394 m_filterKnown = filterKnown;
395 m_invert = invert;
397 if (m_filterEnabled) {
398 // Swap the list of filtered results so we can freely add new items to the list
399 ResultList curFiltered;
400 std::swap(curFiltered, m_filteredOut);
402 // Filter items already on the list
403 for (int i = 0; i < GetItemCount();) {
404 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(i));
406 if (IsFiltered(file)) {
407 ++i;
408 } else {
409 m_filteredOut.push_back(file);
410 DeleteItem(i);
414 // Check the previously filtered items.
415 ResultList::iterator it = curFiltered.begin();
416 for (; it != curFiltered.end(); ++it) {
417 if (IsFiltered(*it)) {
418 AddResult(*it);
419 } else {
420 m_filteredOut.push_back(*it);
427 void CSearchListCtrl::EnableFiltering(bool enabled)
429 if (enabled != m_filterEnabled) {
430 m_filterEnabled = enabled;
432 if (enabled) {
433 SetFilter(m_filterText, m_invert, m_filterKnown);
434 } else {
435 ResultList::iterator it = m_filteredOut.begin();
436 for (; it != m_filteredOut.end(); ++it) {
437 AddResult(*it);
440 m_filteredOut.clear();
446 size_t CSearchListCtrl::GetHiddenItemCount() const
448 return m_filteredOut.size();
452 bool CSearchListCtrl::IsFiltered(const CSearchFile* file)
454 // By default, everything is displayed
455 bool result = true;
457 if (m_filterEnabled && m_filter.IsValid()) {
458 result = m_filter.Matches(file->GetFileName().GetPrintable());
459 result = ((result && !m_invert) || (!result && m_invert));
460 if (result && m_filterKnown) {
461 result = file->GetDownloadStatus() == CSearchFile::NEW;
465 return result;
469 int CSearchListCtrl::SortProc(wxUIntPtr item1, wxUIntPtr item2, long sortData)
471 CSearchFile* file1 = reinterpret_cast<CSearchFile*>(item1);
472 CSearchFile* file2 = reinterpret_cast<CSearchFile*>(item2);
474 // Modifies the result, 1 for ascending, -1 for decending
475 int modifier = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
476 bool alternate = (sortData & CMuleListCtrl::SORT_ALT) != 0;
478 // Decide if which should files we should sort by.
479 wxUIntPtr parent1 = reinterpret_cast<wxUIntPtr>(file1->GetParent());
480 wxUIntPtr parent2 = reinterpret_cast<wxUIntPtr>(file2->GetParent());
481 wxUIntPtr filePtr1 = reinterpret_cast<wxUIntPtr>(file1);
482 wxUIntPtr filePtr2 = reinterpret_cast<wxUIntPtr>(file2);
483 if (parent1 && parent2) {
484 if (parent1 != parent2) {
485 return SortProc(parent1, parent2, sortData);
487 } else if (parent1) {
488 if (parent1 == filePtr2) {
489 return 1;
490 } else {
491 return SortProc(parent1, filePtr2, sortData);
493 } else if (parent2) {
494 if (parent2 == filePtr1) {
495 return -1;
496 } else {
497 return SortProc(filePtr1, parent2, sortData);
501 int result = 0;
502 switch (sortData & CMuleListCtrl::COLUMN_MASK) {
503 // Sort by filename
504 case ID_SEARCH_COL_NAME:
505 result = CmpAny(file1->GetFileName(), file2->GetFileName());
506 break;
508 // Sort file-size
509 case ID_SEARCH_COL_SIZE:
510 result = CmpAny( file1->GetFileSize(), file2->GetFileSize() );
511 break;
513 // Sort by sources
514 case ID_SEARCH_COL_SOURCES: {
515 int cmp = CmpAny( file1->GetSourceCount(), file2->GetSourceCount() );
516 int cmp2 = CmpAny( file1->GetCompleteSourceCount(), file2->GetCompleteSourceCount() );
518 if ( alternate ) {
519 // Swap criterias
520 int temp = cmp2;
521 cmp2 = cmp;
522 cmp = temp;
525 if ( cmp == 0 ) {
526 cmp = cmp2;
529 result = cmp;
530 break;
533 // Sort by file-types
534 case ID_SEARCH_COL_TYPE: {
535 result = GetFiletypeByName(file1->GetFileName()).Cmp(GetFiletypeByName(file2->GetFileName()));
536 if (result == 0) {
537 // Same file-type, sort by extension
538 result = CmpAny(file1->GetFileName().GetExt(), file2->GetFileName().GetExt());
541 break;
544 // Sort by file-hash
545 case ID_SEARCH_COL_FILEID:
546 result = CmpAny(file2->GetFileHash(), file1->GetFileHash());
547 break;
549 // Sort by file status
550 case ID_SEARCH_COL_STATUS:
551 result = CmpAny(DetermineStatusPrintable(file2), DetermineStatusPrintable(file1));
552 break;
554 // Sort by directory
555 case ID_SEARCH_COL_DIRECTORY:
556 result = CmpAny(file1->GetDirectory(), file2->GetDirectory());
557 if (result == 0) { // if equal sort by name
558 result = CmpAny(file1->GetFileName(), file2->GetFileName());
560 break;
563 return modifier * result;
567 void CSearchListCtrl::SetSorting(unsigned column, unsigned order)
569 Freeze();
570 // First collapse all parent items
571 // Backward order means our index won't be influenced by items getting collapsed.
572 for (int i = GetItemCount(); i--;) {
573 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(i));
574 if (file->ShowChildren()) {
575 ShowChildren(file, false);
579 // Then do the sorting
580 CMuleListCtrl::SetSorting(column, order);
581 Thaw();
585 void CSearchListCtrl::SyncLists( CSearchListCtrl* src, CSearchListCtrl* dst )
587 wxCHECK_RET(src && dst, wxT("NULL argument in SyncLists"));
589 // Column widths
590 for ( int i = 0; i < src->GetColumnCount(); i++ ) {
591 // We do this check since just setting the width causes a redraw
592 if ( dst->GetColumnWidth( i ) != src->GetColumnWidth( i ) ) {
593 dst->SetColumnWidth( i, src->GetColumnWidth( i ) );
597 // Sync sorting
598 unsigned column = src->GetSortColumn();
599 unsigned order = src->GetSortOrder();
600 if (column != dst->GetSortColumn() || order != dst->GetSortOrder()) {
601 dst->SetSorting(column, order);
606 void CSearchListCtrl::SyncOtherLists(CSearchListCtrl *src)
608 std::list<CSearchListCtrl*>::iterator it;
610 for (it = s_lists.begin(); it != s_lists.end(); ++it) {
611 if ((*it) != src) {
612 SyncLists( src, *it );
618 void CSearchListCtrl::OnRightClick(wxListEvent& event)
620 CheckSelection(event);
622 if (GetSelectedItemCount()) {
623 // Create the popup-menu
624 wxMenu menu(_("File"));
625 menu.Append(MP_RESUME, _("Download"));
627 wxMenu* cats = new wxMenu(_("Category"));
628 cats->Append(MP_ASSIGNCAT, _("Main"));
629 for (unsigned i = 1; i < theApp->glob_prefs->GetCatCount(); i++) {
630 cats->Append(MP_ASSIGNCAT + i,
631 theApp->glob_prefs->GetCategory(i)->title);
634 menu.Append(MP_MENU_CATS, _("Download in category"), cats);
635 menu.AppendSeparator();
637 const wxString & statsServer = thePrefs::GetStatsServerName();
638 if (!statsServer.IsEmpty()) {
639 menu.Append(MP_RAZORSTATS, CFormat(_("Get %s for this file")) % statsServer);
640 menu.AppendSeparator();
643 menu.Append(MP_SEARCHRELATED, _("Search related files (eD2k, local server)"));
644 menu.AppendSeparator();
646 //#warning Uncomment this here to test the MP_MARK_AS_KNOWN feature. Beware! You are on your own here, this might break "known.met"
647 #if 0
648 menu.Append(MP_MARK_AS_KNOWN, _("Mark as known file"));
649 menu.AppendSeparator();
650 #endif
652 menu.Append(MP_GETED2KLINK, _("Copy eD2k link to clipboard"));
654 // These should only be enabled for single-selections
655 bool enable = (GetSelectedItemCount() == 1);
656 menu.Enable(MP_GETED2KLINK, enable);
657 menu.Enable(MP_MENU_CATS, (theApp->glob_prefs->GetCatCount() > 1));
659 PopupMenu(&menu, event.GetPoint());
660 } else {
661 event.Skip();
666 void CSearchListCtrl::OnColumnLClick( wxListEvent& event )
668 // Let the real event handler do its work first
669 CMuleListCtrl::OnColumnLClick( event );
671 SyncOtherLists( this );
675 void CSearchListCtrl::OnColumnResize( wxListEvent& WXUNUSED(event) )
677 SyncOtherLists( this );
681 void CSearchListCtrl::OnPopupGetUrl( wxCommandEvent& WXUNUSED(event) )
683 wxString URIs;
685 long index = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
687 while (index != -1) {
688 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(index));
690 URIs += theApp->CreateED2kLink( file ) + wxT("\n");
692 index = GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
695 if (!URIs.IsEmpty()) {
696 theApp->CopyTextToClipboard( URIs.RemoveLast() );
701 void CSearchListCtrl::OnRazorStatsCheck( wxCommandEvent& WXUNUSED(event) )
703 int item = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
704 if (item == -1) {
705 return;
708 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(item));
709 theApp->amuledlg->LaunchUrl(thePrefs::GetStatsServerURL() + file->GetFileHash().Encode());
713 void CSearchListCtrl::OnRelatedSearch( wxCommandEvent& WXUNUSED(event) )
715 int item = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
716 if (item == -1) {
717 return;
720 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(item));
721 theApp->searchlist->StopSearch(true);
722 theApp->amuledlg->m_searchwnd->ResetControls();
723 CastByID( IDC_SEARCHNAME, theApp->amuledlg->m_searchwnd, wxTextCtrl )->
724 SetValue(wxT("related::") + file->GetFileHash().Encode());
725 theApp->amuledlg->m_searchwnd->StartNewSearch();
729 void CSearchListCtrl::OnMarkAsKnown( wxCommandEvent& WXUNUSED(event) )
731 #ifndef CLIENT_GUI
732 long index = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
733 while (index > -1) {
734 CSearchFile *searchFile = reinterpret_cast<CSearchFile *>(GetItemData(index));
735 CKnownFile *knownFile(new CKnownFile(*searchFile));
736 theApp->knownfiles->SafeAddKFile(knownFile);
737 index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
739 #endif
743 void CSearchListCtrl::OnPopupDownload(wxCommandEvent& event)
745 if (event.GetId() == MP_RESUME) {
746 // Via the "Download" menu-item, use category specified in drop-down menu
747 DownloadSelected();
748 } else {
749 // Via an "Download in category" item
750 DownloadSelected(event.GetId() - MP_ASSIGNCAT);
755 void CSearchListCtrl::OnItemActivated(wxListEvent& event)
757 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(event.GetIndex()));
758 if (file->HasChildren()) {
759 ShowChildren(file, !file->ShowChildren());
760 } else {
761 DownloadSelected();
766 bool CSearchListCtrl::AltSortAllowed(unsigned column) const
768 switch (column) {
769 case ID_SEARCH_COL_SOURCES:
770 return true;
771 default:
772 return false;
777 void CSearchListCtrl::DownloadSelected(int category)
779 FindWindowById(IDC_SDOWNLOAD)->Enable(FALSE);
781 // Either the "Download" menu-item, the download-button, double-click or enter
782 if (category == -1) {
783 // Defaults to main category
784 category = 0;
786 if (CastByID(IDC_EXTENDEDSEARCHCHECK, NULL, wxCheckBox)->GetValue()) {
787 category = CastByID(ID_AUTOCATASSIGN, NULL, wxChoice)->GetSelection();
791 // Process all selections
792 long index = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
793 while (index > -1) {
794 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(index));
795 CoreNotify_Search_Add_Download(file, category);
796 index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
798 // Listcontrol gets updated by notification when download is started
802 static const wxBrush& GetBrush(wxSystemColour index)
804 return CMuleColour(index).GetBrush();
808 void CSearchListCtrl::OnDrawItem(
809 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
811 CSearchFile* file = reinterpret_cast<CSearchFile*>(GetItemData(item));
813 // Define text-color and background
814 if (highlighted) {
815 if (GetFocus()) {
816 dc->SetBackground(GetBrush(wxSYS_COLOUR_HIGHLIGHT));
817 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
818 } else {
819 dc->SetBackground(GetBrush(wxSYS_COLOUR_BTNSHADOW));
820 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
822 } else {
823 dc->SetBackground(GetBrush(wxSYS_COLOUR_LISTBOX));
824 dc->SetTextForeground(CMuleColour(wxSYS_COLOUR_WINDOWTEXT));
827 // Define the border of the drawn area
828 if (highlighted) {
829 dc->SetPen(*(wxThePenList->FindOrCreatePen(CMuleColour(dc->GetBackground().GetColour()).Blend(65), 1, wxSOLID)));
830 } else {
831 dc->SetPen(*wxTRANSPARENT_PEN);
832 dc->SetTextForeground(GetItemTextColour(item));
835 // Clear the background, not done automatically since the drawing is buffered.
836 dc->SetBrush( dc->GetBackground() );
837 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
839 // Various constant values we use
840 const int iTextOffset = ( rect.GetHeight() - dc->GetCharHeight() ) / 2;
841 const int iOffset = 4;
842 const int treeOffset = 11;
843 const int treeCenter = 6;
844 bool tree_show = false;
846 wxRect cur_rec(iOffset, rect.y, 0, rect.height );
847 for (int i = 0; i < GetColumnCount(); i++) {
848 wxListItem listitem;
849 GetColumn(i, listitem);
851 if ( listitem.GetWidth() > 0 ) {
852 cur_rec.width = listitem.GetWidth() - 2*iOffset;
854 // Make a copy of the current rectangle so we can apply specific tweaks
855 wxRect target_rec = cur_rec;
857 // will ensure that text is about in the middle ;)
858 target_rec.y += iTextOffset;
860 if (i == 0) {
861 if (file->HasChildren() || file->GetParent()) {
862 tree_show = (listitem.GetWidth() > 0);
863 target_rec.x += treeOffset;
864 target_rec.width -= treeOffset;
866 // Children are indented a bit
867 if (file->GetParent()) {
868 target_rec.x += 4;
869 target_rec.width -= 4;
873 // Check if the rating icon should be drawn
874 if (file->HasRating()) {
875 int image = Client_InvalidRating_Smiley + file->UserRating() - 1;
877 int imgWidth = 16;
879 theApp->amuledlg->m_imagelist.Draw(image, *dc, target_rec.GetX(),
880 target_rec.GetY() - 1, wxIMAGELIST_DRAW_TRANSPARENT);
882 // Move the text past the icon.
883 target_rec.x += imgWidth + 4;
884 target_rec.width -= imgWidth + 4;
888 wxListItem cellitem;
889 cellitem.SetColumn(i);
890 cellitem.SetId(item);
892 // Force clipper (clip 2 px more than the rectangle from the right side)
893 wxDCClipper clipper(*dc, target_rec.x, target_rec.y, target_rec.width - 2, target_rec.height);
895 if (GetItem(cellitem)) {
896 dc->DrawText(cellitem.GetText(), target_rec.GetX(), target_rec.GetY());
897 } else {
898 dc->DrawText(wxT("GetItem failed!"), target_rec.GetX(), target_rec.GetY());
901 // Increment to the next column
902 cur_rec.x += listitem.GetWidth();
906 // Draw tree last so it draws over selected and focus (looks better)
907 if (tree_show) {
908 // Gather some information
909 const bool notLast = (item + 1 < GetItemCount());
910 const bool notFirst = (item != 0);
911 const bool hasNext = notLast && reinterpret_cast<CSearchFile*>(GetItemData(item + 1))->GetParent();
912 const int middle = cur_rec.y + ( cur_rec.height + 1 ) / 2;
914 // Set up a new pen for drawing the tree
915 dc->SetPen( *(wxThePenList->FindOrCreatePen(dc->GetTextForeground(), 1, wxSOLID)) );
917 if (file->GetParent()) {
918 // Draw the line to the filename
919 dc->DrawLine(treeCenter, middle, treeOffset + 4, middle);
921 // Draw the line to the child node
922 if (hasNext) {
923 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y + cur_rec.height + 1);
926 // Draw the line back up to parent node
927 if (notFirst) {
928 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y - 1);
930 } else if (file->HasChildren()) {
931 if (file->ShowChildren()) {
932 // Draw empty circle
933 dc->SetBrush(*wxTRANSPARENT_BRUSH);
934 } else {
935 dc->SetBrush(*(wxTheBrushList->FindOrCreateBrush(GetItemTextColour(item))));
938 dc->DrawCircle( treeCenter, middle, 3 );
940 // Draw the line to the child node if there are any children
941 if (hasNext && file->ShowChildren()) {
942 dc->DrawLine(treeCenter, middle + 3, treeCenter, cur_rec.y + cur_rec.height + 1);
947 // Sanity checks to ensure that results/children are properly positioned.
948 #ifdef __WXDEBUG__
950 CSearchFile* parent = file->GetParent();
952 if (item > 0) {
953 CSearchFile* before = reinterpret_cast<CSearchFile*>(GetItemData(item - 1));
954 wxASSERT(before);
955 if (parent) {
956 wxASSERT((before->GetParent() == parent) || (before == parent));
957 } else {
958 wxASSERT(before->GetParent() != file);
962 if (item < GetItemCount() - 1) {
963 CSearchFile* after = reinterpret_cast<CSearchFile*>(GetItemData(item + 1));
964 wxASSERT(after);
965 if (parent) {
966 wxASSERT((after->GetParent() == parent) || (!after->GetParent()));
967 } else {
968 wxASSERT((after->GetParent() == file) || (!after->GetParent()));
972 #endif
976 void CSearchListCtrl::ShowChildren(CSearchFile* file, bool show)
978 Freeze();
980 file->SetShowChildren(show);
982 const CSearchResultList& results = file->GetChildren();
983 for (size_t i = 0; i < results.size(); ++i) {
984 if (show) {
985 AddResult(results[i]);
986 } else {
987 RemoveResult(results[i]);
991 Thaw();
995 wxString CSearchListCtrl::GetTTSText(unsigned item) const
997 return GetItemText(item);
1001 wxString CSearchListCtrl::DetermineStatusPrintable(CSearchFile *toshow)
1003 switch (toshow->GetDownloadStatus()) {
1004 case CSearchFile::DOWNLOADED:
1005 // File has already been downloaded.
1006 return _("Downloaded");
1007 case CSearchFile::QUEUED:
1008 // File is downloading.
1009 case CSearchFile::QUEUEDCANCELED:
1010 // File is downloading and has been canceled before.
1011 return _("Queued");
1012 case CSearchFile::CANCELED:
1013 // File has been canceled.
1014 return _("Canceled");
1015 default:
1016 // File is new.
1017 return _("New");
1020 // File_checked_for_headers