Upstream tarball 20080324
[amule.git] / src / SearchListCtrl.cpp
blob1a095c55d70bb10d7def8c148ffb032196fa29c3
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 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
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
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);
88 InsertColumn( ID_SEARCH_COL_SIZE, _("Size"), wxLIST_FORMAT_LEFT, 100);
89 InsertColumn( ID_SEARCH_COL_SOURCES, _("Sources"), wxLIST_FORMAT_LEFT, 50);
90 InsertColumn( ID_SEARCH_COL_TYPE, _("Type"), wxLIST_FORMAT_LEFT, 65);
91 InsertColumn( ID_SEARCH_COL_FILEID, _("FileID"), wxLIST_FORMAT_LEFT, 280);
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 CSearchListCtrl::~CSearchListCtrl()
116 std::list<CSearchListCtrl*>::iterator it = std::find( s_lists.begin(), s_lists.end(), this );
118 if ( it != s_lists.end() )
119 s_lists.erase( it );
121 // We only save the settings if the last list was closed
122 if ( s_lists.empty() ) {
123 // In order to get the settings saved, we need to set the name
124 SetTableName( wxT("Search") );
129 void CSearchListCtrl::AddResult(CSearchFile* toshow)
131 wxCHECK_RET(toshow->GetSearchID() == m_nResultsID, wxT("Wrong search-id for result-list"));
133 const wxUIntPtr toshowdata = reinterpret_cast<wxUIntPtr>(toshow);
135 // Check if the result should be shown
136 if (FindItem(-1, toshowdata) != -1) {
137 return;
138 } else if (toshow->GetParent() && !toshow->GetParent()->ShowChildren()) {
139 return;
140 } else if (!IsFiltered(toshow)) {
141 if (toshow->HasChildren() && toshow->ShowChildren()) {
142 // Only filter the parent if none of the children are shown.
143 bool foundChild = false;
144 const CSearchResultList& children = toshow->GetChildren();
145 for (size_t i = 0; i < children.size(); ++i) {
146 if (IsFiltered(children.at(i))) {
147 foundChild = true;
148 break;
152 if (!foundChild) {
153 // No children left, and the parent is filtered.
154 m_filteredOut.push_back(toshow);
155 return;
157 } else {
158 m_filteredOut.push_back(toshow);
159 return;
163 // Insert the item before the item found by the search
164 uint32 newid = InsertItem(GetInsertPos(toshowdata), toshow->GetFileName().GetPrintable());
166 // Sanity checks to ensure that results/children are properly positioned.
167 #ifdef __WXDEBUG__
169 CSearchFile* parent = toshow->GetParent();
171 if (newid > 0) {
172 CSearchFile* before = (CSearchFile*)GetItemData(newid - 1);
173 wxASSERT(before);
174 if (parent) {
175 wxASSERT((before->GetParent() == parent) || (before == parent));
176 } else {
177 wxASSERT(before->GetParent() != toshow);
181 if ((int)newid < GetItemCount() - 1) {
182 CSearchFile* after = (CSearchFile*)GetItemData(newid + 1);
183 wxASSERT(after);
184 if (parent) {
185 wxASSERT((after->GetParent() == parent) || (!after->GetParent()));
186 } else {
187 wxASSERT((after->GetParent() == toshow) || (!after->GetParent()));
191 #endif
193 SetItemPtrData(newid, toshowdata);
195 // Filesize
196 SetItem(newid, ID_SEARCH_COL_SIZE, CastItoXBytes( toshow->GetFileSize() ) );
198 // Source count
199 wxString temp = wxString::Format( wxT("%d (%d)"), toshow->GetSourceCount(), toshow->GetCompleteSourceCount() );
200 SetItem( newid, ID_SEARCH_COL_SOURCES, temp );
202 // File-type
203 SetItem( newid, ID_SEARCH_COL_TYPE, GetFiletypeByName( toshow->GetFileName() ) );
205 // File-hash
206 SetItem(newid, ID_SEARCH_COL_FILEID, toshow->GetFileHash().Encode() );
208 // Set the color of the item
209 UpdateItemColor( newid );
213 void CSearchListCtrl::RemoveResult(CSearchFile* toremove)
215 ShowChildren(toremove, false);
217 long index = FindItem(-1, reinterpret_cast<wxUIntPtr>(toremove));
218 if (index != -1) {
219 DeleteItem(index);
220 } else {
221 ResultList::iterator it = std::find(m_filteredOut.begin(), m_filteredOut.end(), toremove);
222 if ( it != m_filteredOut.end()) {
223 m_filteredOut.erase(it);
229 void CSearchListCtrl::UpdateResult(CSearchFile* toupdate)
231 long index = FindItem(-1, reinterpret_cast<wxUIntPtr>(toupdate));
232 if (index != -1) {
233 // Update the filename, which may be changed in case of multiple variants.
234 SetItem(index, ID_SEARCH_COL_NAME, toupdate->GetFileName().GetPrintable());
236 wxString temp = wxString::Format( wxT("%d (%d)"), toupdate->GetSourceCount(), toupdate->GetCompleteSourceCount());
237 SetItem(index, ID_SEARCH_COL_SOURCES, temp);
239 UpdateItemColor(index);
241 // Deletions of items causes rather large ammount of flicker, so to
242 // avoid this, we resort the list to ensure correct ordering.
243 if (!IsItemSorted(index)) {
244 SortList();
250 void CSearchListCtrl::UpdateItemColor( long index )
252 wxListItem item;
253 item.SetId( index );
254 item.SetColumn( ID_SEARCH_COL_SIZE );
255 item.SetMask( wxLIST_MASK_STATE|wxLIST_MASK_TEXT|wxLIST_MASK_IMAGE|wxLIST_MASK_DATA|wxLIST_MASK_WIDTH|wxLIST_MASK_FORMAT );
257 if ( GetItem(item) ) {
258 wxColour newcol = SYSCOLOR(wxSYS_COLOUR_WINDOWTEXT);
260 CSearchFile* file = (CSearchFile*)GetItemData(index);
261 CKnownFile* sameFile = theApp->downloadqueue->GetFileByID(file->GetFileHash());
262 if ( !sameFile ) {
263 sameFile = theApp->knownfiles->FindKnownFileByID(file->GetFileHash());
266 int red = newcol.Red();
267 int green = newcol.Green();
268 int blue = newcol.Blue();
270 if ( sameFile ) {
271 if ( sameFile->IsPartFile() ) {
272 // File is already being downloaded. Mark as red.
273 red = 255;
274 } else if ( sameFile->GetStatus() == PS_COMPLETE ) {
275 // File has already been downloaded. Mark as green.
276 green = 200;
277 } else {
278 // File has been cancelled or removed. Mark as grey.
279 red = 128;
280 green = 128;
281 blue = 128;
283 } else {
284 // File is new, colour after number of files
285 blue += file->GetSourceCount() * 5;
286 if ( blue > 255 ) {
287 blue = 255;
291 // don't forget to set the item data back...
292 wxListItem newitem;
293 newitem.SetId( index );
294 newitem.SetTextColour( wxColour( red, green, blue ) );
295 SetItem( newitem );
300 // Update the colors of all assosiated items, which means parents and/or siblings.
301 void CSearchListCtrl::UpdateAllRelativesColor(
302 CSearchFile *file,
303 long index)
305 if ((file->ShowChildren() && file->HasChildren()) ||
306 file->GetParent()) {
307 CSearchFile *parent = file->GetParent() ?
308 file->GetParent() : file;
309 const CSearchResultList &list = parent->GetChildren();
310 for (size_t j = 0; j < list.size(); ++j) {
311 UpdateItemColor(FindItem(-1, reinterpret_cast<wxUIntPtr>(list.at(j))));
313 UpdateItemColor(FindItem(-1, reinterpret_cast<wxUIntPtr>(parent)));
314 } else {
315 UpdateItemColor(index);
320 void CSearchListCtrl::ShowResults( long ResultsID )
322 DeleteAllItems();
323 m_nResultsID = ResultsID;
324 if (ResultsID) {
325 const CSearchResultList& list = theApp->searchlist->GetSearchResults(ResultsID);
326 for (unsigned int i = 0; i < list.size(); ++i) {
327 AddResult( list[i] );
333 wxUIntPtr CSearchListCtrl::GetSearchId()
335 return m_nResultsID;
339 void CSearchListCtrl::SetFilter(const wxString& regExp, bool invert, bool filterKnown)
341 if (regExp.IsEmpty()) {
342 // Show everything
343 m_filterText = wxT(".*");
344 } else {
345 m_filterText = regExp;
348 m_filter.Compile(m_filterText, wxRE_DEFAULT | wxRE_ICASE);
349 m_filterKnown = filterKnown;
350 m_invert = invert;
352 if (m_filterEnabled) {
353 // Swap the list of filtered results so we can freely add new items to the list
354 ResultList curFiltered;
355 std::swap(curFiltered, m_filteredOut);
357 // Filter items already on the list
358 for (int i = 0; i < GetItemCount();) {
359 CSearchFile* file = (CSearchFile*)GetItemData(i);
361 if (IsFiltered(file)) {
362 ++i;
363 } else {
364 m_filteredOut.push_back(file);
365 DeleteItem(i);
369 // Check the previously filtered items.
370 ResultList::iterator it = curFiltered.begin();
371 for (; it != curFiltered.end(); ++it) {
372 if (IsFiltered(*it)) {
373 AddResult(*it);
374 } else {
375 m_filteredOut.push_back(*it);
382 void CSearchListCtrl::EnableFiltering(bool enabled)
384 if (enabled != m_filterEnabled) {
385 m_filterEnabled = enabled;
387 if (enabled) {
388 SetFilter(m_filterText, m_invert, m_filterKnown);
389 } else {
390 ResultList::iterator it = m_filteredOut.begin();
391 for (; it != m_filteredOut.end(); ++it) {
392 AddResult(*it);
395 m_filteredOut.clear();
401 size_t CSearchListCtrl::GetHiddenItemCount() const
403 return m_filteredOut.size();
407 bool CSearchListCtrl::IsFiltered(const CSearchFile* file)
409 // By default, everything is displayed
410 bool result = true;
412 if (m_filterEnabled && m_filter.IsValid()) {
413 result = m_filter.Matches(file->GetFileName().GetPrintable());
414 result = ((result && !m_invert) || (!result && m_invert));
416 if (result && m_filterKnown) {
417 result = !theApp->downloadqueue->GetFileByID(file->GetFileHash());
419 if (result) {
420 result = !theApp->knownfiles->FindKnownFileByID(file->GetFileHash());
425 return result;
429 int CSearchListCtrl::SortProc(wxUIntPtr item1, wxUIntPtr item2, long sortData)
431 CSearchFile* file1 = (CSearchFile*)item1;
432 CSearchFile* file2 = (CSearchFile*)item2;
434 // Modifies the result, 1 for ascending, -1 for decending
435 int modifier = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
436 bool alternate = (sortData & CMuleListCtrl::SORT_ALT);
438 // Decide if which should files we should sort by.
439 wxUIntPtr parent1 = reinterpret_cast<wxUIntPtr>(file1->GetParent());
440 wxUIntPtr parent2 = reinterpret_cast<wxUIntPtr>(file2->GetParent());
441 wxUIntPtr filePtr1 = reinterpret_cast<wxUIntPtr>(file1);
442 wxUIntPtr filePtr2 = reinterpret_cast<wxUIntPtr>(file2);
443 if (parent1 && parent2) {
444 if (parent1 != parent2) {
445 return SortProc(parent1, parent2, sortData);
447 } else if (parent1) {
448 if (parent1 == filePtr2) {
449 return 1;
450 } else {
451 return SortProc(parent1, filePtr2, sortData);
453 } else if (parent2) {
454 if (parent2 == filePtr1) {
455 return -1;
456 } else {
457 return SortProc(filePtr1, parent2, sortData);
461 int result = 0;
462 switch (sortData & CMuleListCtrl::COLUMN_MASK) {
463 // Sort by filename
464 case ID_SEARCH_COL_NAME:
465 result = CmpAny(file1->GetFileName(), file2->GetFileName());
466 break;
468 // Sort file-size
469 case ID_SEARCH_COL_SIZE:
470 result = CmpAny( file1->GetFileSize(), file2->GetFileSize() );
471 break;
473 // Sort by sources
474 case ID_SEARCH_COL_SOURCES: {
475 int cmp = CmpAny( file1->GetSourceCount(), file2->GetSourceCount() );
476 int cmp2 = CmpAny( file1->GetCompleteSourceCount(), file2->GetCompleteSourceCount() );
478 if ( alternate ) {
479 // Swap criterias
480 int temp = cmp2;
481 cmp2 = cmp;
482 cmp = temp;
485 if ( cmp == 0 ) {
486 cmp = cmp2;
489 result = cmp;
490 break;
493 // Sort by file-types
494 case ID_SEARCH_COL_TYPE: {
495 result = GetFiletypeByName(file1->GetFileName()).Cmp(GetFiletypeByName(file2->GetFileName()));
496 if (result == 0) {
497 // Same file-type, sort by extension
498 result = CmpAny(file1->GetFileName().GetExt(), file2->GetFileName().GetExt());
501 break;
504 // Sort by file-hash
505 case ID_SEARCH_COL_FILEID:
506 result = CmpAny(file2->GetFileHash(), file1->GetFileHash());
509 return modifier * result;
513 void CSearchListCtrl::SyncLists( CSearchListCtrl* src, CSearchListCtrl* dst )
515 wxCHECK_RET(src && dst, wxT("NULL argument in SyncLists"));
517 // Column widths
518 for ( int i = 0; i < src->GetColumnCount(); i++ ) {
519 // We do this check since just setting the width causes a redraw
520 if ( dst->GetColumnWidth( i ) != src->GetColumnWidth( i ) ) {
521 dst->SetColumnWidth( i, src->GetColumnWidth( i ) );
525 // Sync sorting
526 unsigned column = src->GetSortColumn();
527 unsigned order = src->GetSortOrder();
528 if (column != dst->GetSortColumn() || order != dst->GetSortOrder()) {
529 dst->SetSorting(column, order);
534 void CSearchListCtrl::SyncOtherLists( CSearchListCtrl* src )
536 std::list<CSearchListCtrl*>::iterator it;
538 for ( it = s_lists.begin(); it != s_lists.end(); ++it ) {
539 if ( (*it) != src ) {
540 SyncLists( src, *it );
546 void CSearchListCtrl::OnRightClick(wxListEvent& event)
548 CheckSelection(event);
550 if (GetSelectedItemCount()) {
551 // Create the popup-menu
552 wxMenu menu(_("File"));
553 menu.Append(MP_RESUME, _("Download"));
555 wxMenu* cats = new wxMenu(_("Category"));
556 cats->Append(MP_ASSIGNCAT, _("Main"));
557 for (unsigned i = 1; i < theApp->glob_prefs->GetCatCount(); i++) {
558 cats->Append(MP_ASSIGNCAT + i,
559 theApp->glob_prefs->GetCategory(i)->title);
562 menu.Append(MP_MENU_CATS, _("Download in category"), cats);
563 menu.AppendSeparator();
564 /* Commented out while it's gone
565 menu.Append(MP_RAZORSTATS, _("Get Razorback 2's stats for this file"));
566 menu.AppendSeparator();
568 menu.Append(MP_SEARCHRELATED, _("Search related files (ED2k, local server)"));
569 menu.AppendSeparator();
571 //#warning Uncomment this here to test the MP_MARK_AS_KNOWN feature. Beware! You are on your own here, this might break "known.met"
572 #if 0
573 menu.Append(MP_MARK_AS_KNOWN, _("Mark as known file"));
574 menu.AppendSeparator();
575 #endif
577 menu.Append(MP_GETED2KLINK, _("Copy ED2k link to clipboard"));
579 // These should only be enabled for single-selections
580 bool enable = GetSelectedItemCount();
581 menu.Enable(MP_GETED2KLINK, enable);
582 menu.Enable(MP_MENU_CATS, (theApp->glob_prefs->GetCatCount() > 1));
584 PopupMenu(&menu, event.GetPoint());
585 } else {
586 event.Skip();
591 void CSearchListCtrl::OnColumnLClick( wxListEvent& event )
593 // Let the real event handler do its work first
594 CMuleListCtrl::OnColumnLClick( event );
596 SyncOtherLists( this );
600 void CSearchListCtrl::OnColumnResize( wxListEvent& WXUNUSED(event) )
602 SyncOtherLists( this );
606 void CSearchListCtrl::OnPopupGetUrl( wxCommandEvent& WXUNUSED(event) )
608 wxString URIs;
610 long index = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
612 while( index != -1 ) {
613 CSearchFile* file = (CSearchFile*)GetItemData( index );
615 URIs += theApp->CreateED2kLink( file ) + wxT("\n");
617 index = GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
620 if ( !URIs.IsEmpty() ) {
621 theApp->CopyTextToClipboard( URIs.RemoveLast() );
626 void CSearchListCtrl::OnRazorStatsCheck( wxCommandEvent& WXUNUSED(event) )
628 int item = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
629 if ( item == -1 ) {
630 return;
633 CSearchFile* file = (CSearchFile*)GetItemData( item );
634 theApp->amuledlg->LaunchUrl(wxT("http://stats.razorback2.com/ed2khistory?ed2k=") + file->GetFileHash().Encode());
638 void CSearchListCtrl::OnRelatedSearch( wxCommandEvent& WXUNUSED(event) )
640 int item = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
641 if ( item == -1 ) {
642 return;
645 CSearchFile* file = (CSearchFile*)GetItemData( item );
646 theApp->searchlist->StopGlobalSearch();
647 theApp->amuledlg->m_searchwnd->ResetControls();
648 CastByID( IDC_SEARCHNAME, theApp->amuledlg->m_searchwnd, wxTextCtrl )->
649 SetValue(wxT("related::") + file->GetFileHash().Encode());
650 theApp->amuledlg->m_searchwnd->StartNewSearch();
654 void CSearchListCtrl::OnMarkAsKnown( wxCommandEvent& WXUNUSED(event) )
656 int index = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
657 if (index == -1) {
658 return;
661 #ifndef CLIENT_GUI
662 CSearchFile *searchFile = (CSearchFile *)GetItemData(index);
663 CKnownFile *knownFile(new CKnownFile(*searchFile));
664 theApp->knownfiles->SafeAddKFile(knownFile);
665 UpdateAllRelativesColor(searchFile, index);
666 #endif
670 void CSearchListCtrl::OnPopupDownload(wxCommandEvent& event)
672 if (event.GetId() == MP_RESUME) {
673 // Via the "Download" menu-item, use category specified in drop-down menu
674 DownloadSelected();
675 } else {
676 // Via an "Download in category" item
677 DownloadSelected(event.GetId() - MP_ASSIGNCAT);
682 void CSearchListCtrl::OnItemActivated(wxListEvent& event)
684 CSearchFile* file = ((CSearchFile*)GetItemData(event.GetIndex()));
685 if (file->HasChildren()) {
686 ShowChildren(file, !file->ShowChildren());
687 } else {
688 DownloadSelected();
693 bool CSearchListCtrl::AltSortAllowed(unsigned column) const
695 switch ( column ) {
696 case ID_SEARCH_COL_SOURCES:
697 return true;
699 default:
700 return false;
705 void CSearchListCtrl::DownloadSelected(int category)
707 FindWindowById(IDC_SDOWNLOAD)->Enable(FALSE);
709 // Either the "Download" menu-item, the download-button, double-click or enter
710 if (category == -1) {
711 // Defaults to main category
712 category = 0;
714 if (CastByID(IDC_EXTENDEDSEARCHCHECK, NULL, wxCheckBox)->GetValue()) {
715 category = CastByID(ID_AUTOCATASSIGN, NULL, wxChoice)->GetSelection();
719 long index = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
720 while (index > -1) {
721 CSearchFile* file = (CSearchFile*)GetItemData(index);
722 CoreNotify_Search_Add_Download(file, category);
723 UpdateAllRelativesColor(file, index);
724 index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
729 const wxBrush& GetBrush(wxSystemColour index)
731 return *wxTheBrushList->FindOrCreateBrush(SYSCOLOR(index));
735 void CSearchListCtrl::OnDrawItem(
736 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
738 CSearchFile* file = (CSearchFile*)GetItemData(item);
740 // Define text-color and background
741 if (highlighted) {
742 if (GetFocus()) {
743 dc->SetBackground(GetBrush(wxSYS_COLOUR_HIGHLIGHT));
744 dc->SetTextForeground(SYSCOLOR(wxSYS_COLOUR_HIGHLIGHTTEXT));
745 } else {
746 dc->SetBackground(GetBrush(wxSYS_COLOUR_BTNSHADOW));
747 dc->SetTextForeground(SYSCOLOR(wxSYS_COLOUR_HIGHLIGHTTEXT));
749 } else {
750 dc->SetBackground(GetBrush(wxSYS_COLOUR_LISTBOX));
751 dc->SetTextForeground(SYSCOLOR(wxSYS_COLOUR_WINDOWTEXT));
754 // Define the border of the drawn area
755 if ( highlighted ) {
756 dc->SetPen(wxPen(BLEND(dc->GetBackground().GetColour(), 65)));
757 } else {
758 dc->SetPen(*wxTRANSPARENT_PEN);
759 dc->SetTextForeground(GetItemTextColour(item));
762 // Clear the background, not done automatically since the drawing is buffered.
763 dc->SetBrush( dc->GetBackground() );
764 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
766 // Various constant values we use
767 const int iTextOffset = ( rect.GetHeight() - dc->GetCharHeight() ) / 2;
768 const int iOffset = 4;
769 const int treeOffset = 11;
770 const int treeCenter = 6;
771 bool tree_show = false;
773 wxRect cur_rec(iOffset, rect.y, 0, rect.height );
774 for (int i = 0; i < GetColumnCount(); i++) {
775 wxListItem listitem;
776 GetColumn(i, listitem);
778 if ( listitem.GetWidth() > 0 ) {
779 cur_rec.width = listitem.GetWidth() - 2*iOffset;
781 // Make a copy of the current rectangle so we can apply specific tweaks
782 wxRect target_rec = cur_rec;
784 // will ensure that text is about in the middle ;)
785 target_rec.y += iTextOffset;
787 if (i == 0) {
788 if (file->HasChildren() || file->GetParent()) {
789 tree_show = (listitem.GetWidth() > 0);
790 target_rec.x += treeOffset;
791 target_rec.width -= treeOffset;
793 // Children are indented a bit
794 if (file->GetParent()) {
795 target_rec.x += 4;
796 target_rec.width -= 4;
800 // Check if the rating icon should be drawn
801 if (file->HasRating()) {
802 int image = Client_InvalidRating_Smiley + file->UserRating() - 1;
804 int imgWidth = 16;
806 theApp->amuledlg->m_imagelist.Draw(image, *dc, target_rec.GetX(),
807 target_rec.GetY() - 1, wxIMAGELIST_DRAW_TRANSPARENT);
809 // Move the text past the icon.
810 target_rec.x += imgWidth + 4;
811 target_rec.width -= imgWidth + 4;
815 wxListItem cellitem;
816 cellitem.SetColumn(i);
817 cellitem.SetId(item);
819 // Force clipper (clip 2 px more than the rectangle from the right side)
820 wxDCClipper clipper(*dc, target_rec.x, target_rec.y, target_rec.width - 2, target_rec.height);
822 if (GetItem(cellitem)) {
823 dc->DrawText(cellitem.GetText(), target_rec.GetX(), target_rec.GetY());
824 } else {
825 dc->DrawText(wxT("GetItem failed!"), target_rec.GetX(), target_rec.GetY());
828 // Increment to the next column
829 cur_rec.x += listitem.GetWidth();
833 // Draw tree last so it draws over selected and focus (looks better)
834 if ( tree_show ) {
835 // Gather some information
836 const bool notLast = (item + 1 < GetItemCount());
837 const bool notFirst = (item != 0);
838 const bool hasNext = notLast && ((CSearchFile*)GetItemData(item + 1))->GetParent();
839 const int middle = cur_rec.y + ( cur_rec.height + 1 ) / 2;
841 // Set up a new pen for drawing the tree
842 dc->SetPen( *(wxThePenList->FindOrCreatePen(dc->GetTextForeground(), 1, wxSOLID)) );
844 if (file->GetParent()) {
845 // Draw the line to the filename
846 dc->DrawLine(treeCenter, middle, treeOffset + 4, middle);
848 // Draw the line to the child node
849 if (hasNext) {
850 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y + cur_rec.height + 1);
853 // Draw the line back up to parent node
854 if (notFirst) {
855 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y - 1);
857 } else if (file->HasChildren()) {
858 if (file->ShowChildren()) {
859 // Draw empty circle
860 dc->SetBrush(*wxTRANSPARENT_BRUSH);
861 } else {
862 dc->SetBrush(GetItemTextColour(item));
865 dc->DrawCircle( treeCenter, middle, 3 );
867 // Draw the line to the child node if there are any children
868 if (hasNext && file->ShowChildren()) {
869 dc->DrawLine(treeCenter, middle + 3, treeCenter, cur_rec.y + cur_rec.height + 1);
874 // Sanity checks to ensure that results/children are properly positioned.
875 #ifdef __WXDEBUG__
877 CSearchFile* parent = file->GetParent();
879 if (item > 0) {
880 CSearchFile* before = (CSearchFile*)GetItemData(item - 1);
881 wxASSERT(before);
882 if (parent) {
883 wxASSERT((before->GetParent() == parent) || (before == parent));
884 } else {
885 wxASSERT(before->GetParent() != file);
889 if (item < GetItemCount() - 1) {
890 CSearchFile* after = (CSearchFile*)GetItemData(item + 1);
891 wxASSERT(after);
892 if (parent) {
893 wxASSERT((after->GetParent() == parent) || (!after->GetParent()));
894 } else {
895 wxASSERT((after->GetParent() == file) || (!after->GetParent()));
899 #endif
903 void CSearchListCtrl::ShowChildren(CSearchFile* file, bool show)
905 Freeze();
907 file->SetShowChildren(show);
909 const CSearchResultList& results = file->GetChildren();
910 for (size_t i = 0; i < results.size(); ++i) {
911 if (show) {
912 AddResult(results[i]);
913 } else {
914 RemoveResult(results[i]);
918 Thaw();
922 wxString CSearchListCtrl::GetTTSText(unsigned item) const
924 return GetItemText(item);
926 // File_checked_for_headers