2 // This file is part of the aMule Project.
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 )
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
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 "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
)
58 std::list
<CSearchListCtrl
*> CSearchListCtrl::s_lists
;
61 enum SearchListColumns
{
62 ID_SEARCH_COL_NAME
= 0,
64 ID_SEARCH_COL_SOURCES
,
70 CSearchListCtrl::CSearchListCtrl(
76 const wxValidator
&validator
,
79 CMuleListCtrl(parent
, winid
, pos
, size
, style
| wxLC_OWNERDRAW
, validator
, name
),
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);
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") );
102 // Unset the name to avoid the settings getting saved every time a list is closed
103 SetTableName( wxEmptyString
);
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() )
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) {
138 } else if (toshow
->GetParent() && !toshow
->GetParent()->ShowChildren()) {
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
))) {
153 // No children left, and the parent is filtered.
154 m_filteredOut
.push_back(toshow
);
158 m_filteredOut
.push_back(toshow
);
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.
169 CSearchFile
* parent
= toshow
->GetParent();
172 CSearchFile
* before
= (CSearchFile
*)GetItemData(newid
- 1);
175 wxASSERT((before
->GetParent() == parent
) || (before
== parent
));
177 wxASSERT(before
->GetParent() != toshow
);
181 if ((int)newid
< GetItemCount() - 1) {
182 CSearchFile
* after
= (CSearchFile
*)GetItemData(newid
+ 1);
185 wxASSERT((after
->GetParent() == parent
) || (!after
->GetParent()));
187 wxASSERT((after
->GetParent() == toshow
) || (!after
->GetParent()));
193 SetItemPtrData(newid
, toshowdata
);
196 SetItem(newid
, ID_SEARCH_COL_SIZE
, CastItoXBytes( toshow
->GetFileSize() ) );
199 wxString temp
= wxString::Format( wxT("%d (%d)"), toshow
->GetSourceCount(), toshow
->GetCompleteSourceCount() );
201 if (toshow
->GetKadPublishInfo() == 0) {
204 temp
+= wxString::Format(wxT(" | N:%u, P:%u, T:%0.2f"), (toshow
->GetKadPublishInfo() & 0xFF000000) >> 24, (toshow
->GetKadPublishInfo() & 0x00FF0000) >> 16, (toshow
->GetKadPublishInfo() & 0x0000FFFF) / 100.0);
207 SetItem( newid
, ID_SEARCH_COL_SOURCES
, temp
);
210 SetItem( newid
, ID_SEARCH_COL_TYPE
, GetFiletypeByName( toshow
->GetFileName() ) );
213 SetItem(newid
, ID_SEARCH_COL_FILEID
, toshow
->GetFileHash().Encode() );
215 // Set the color of the item
216 UpdateItemColor( newid
);
220 void CSearchListCtrl::RemoveResult(CSearchFile
* toremove
)
222 ShowChildren(toremove
, false);
224 long index
= FindItem(-1, reinterpret_cast<wxUIntPtr
>(toremove
));
228 ResultList::iterator it
= std::find(m_filteredOut
.begin(), m_filteredOut
.end(), toremove
);
229 if ( it
!= m_filteredOut
.end()) {
230 m_filteredOut
.erase(it
);
236 void CSearchListCtrl::UpdateResult(CSearchFile
* toupdate
)
238 long index
= FindItem(-1, reinterpret_cast<wxUIntPtr
>(toupdate
));
240 // Update the filename, which may be changed in case of multiple variants.
241 SetItem(index
, ID_SEARCH_COL_NAME
, toupdate
->GetFileName().GetPrintable());
243 wxString temp
= wxString::Format( wxT("%d (%d)"), toupdate
->GetSourceCount(), toupdate
->GetCompleteSourceCount());
245 if (toupdate
->GetKadPublishInfo() == 0) {
248 temp
+= wxString::Format(wxT(" | N:%u, P:%u, T:%0.2f"), (toupdate
->GetKadPublishInfo() & 0xFF000000) >> 24, (toupdate
->GetKadPublishInfo() & 0x00FF0000) >> 16, (toupdate
->GetKadPublishInfo() & 0x0000FFFF) / 100.0);
251 SetItem(index
, ID_SEARCH_COL_SOURCES
, temp
);
253 UpdateItemColor(index
);
255 // Deletions of items causes rather large amount of flicker, so to
256 // avoid this, we resort the list to ensure correct ordering.
257 if (!IsItemSorted(index
)) {
264 void CSearchListCtrl::UpdateItemColor( long index
)
268 item
.SetColumn( ID_SEARCH_COL_SIZE
);
269 item
.SetMask( wxLIST_MASK_STATE
|wxLIST_MASK_TEXT
|wxLIST_MASK_IMAGE
|wxLIST_MASK_DATA
|wxLIST_MASK_WIDTH
|wxLIST_MASK_FORMAT
);
271 if ( GetItem(item
) ) {
272 wxColour newcol
= SYSCOLOR(wxSYS_COLOUR_WINDOWTEXT
);
274 CSearchFile
* file
= (CSearchFile
*)GetItemData(index
);
275 CKnownFile
* sameFile
= theApp
->downloadqueue
->GetFileByID(file
->GetFileHash());
277 sameFile
= theApp
->knownfiles
->FindKnownFileByID(file
->GetFileHash());
280 int red
= newcol
.Red();
281 int green
= newcol
.Green();
282 int blue
= newcol
.Blue();
285 if ( sameFile
->IsPartFile() ) {
286 // File is already being downloaded. Mark as red.
288 } else if ( sameFile
->GetStatus() == PS_COMPLETE
) {
289 // File has already been downloaded. Mark as green.
292 // File has been cancelled or removed. Mark as grey.
298 // File is new, colour after number of files
299 blue
+= file
->GetSourceCount() * 5;
305 // don't forget to set the item data back...
307 newitem
.SetId( index
);
308 newitem
.SetTextColour( wxColour( red
, green
, blue
) );
314 // Update the colors of all assosiated items, which means parents and/or siblings.
315 void CSearchListCtrl::UpdateAllRelativesColor(
319 if ((file
->ShowChildren() && file
->HasChildren()) ||
321 CSearchFile
*parent
= file
->GetParent() ?
322 file
->GetParent() : file
;
323 const CSearchResultList
&list
= parent
->GetChildren();
324 for (size_t j
= 0; j
< list
.size(); ++j
) {
325 UpdateItemColor(FindItem(-1, reinterpret_cast<wxUIntPtr
>(list
.at(j
))));
327 UpdateItemColor(FindItem(-1, reinterpret_cast<wxUIntPtr
>(parent
)));
329 UpdateItemColor(index
);
334 void CSearchListCtrl::ShowResults( long ResultsID
)
337 m_nResultsID
= ResultsID
;
339 const CSearchResultList
& list
= theApp
->searchlist
->GetSearchResults(ResultsID
);
340 for (unsigned int i
= 0; i
< list
.size(); ++i
) {
341 AddResult( list
[i
] );
347 wxUIntPtr
CSearchListCtrl::GetSearchId()
353 void CSearchListCtrl::SetFilter(const wxString
& regExp
, bool invert
, bool filterKnown
)
355 if (regExp
.IsEmpty()) {
357 m_filterText
= wxT(".*");
359 m_filterText
= regExp
;
362 m_filter
.Compile(m_filterText
, wxRE_DEFAULT
| wxRE_ICASE
);
363 m_filterKnown
= filterKnown
;
366 if (m_filterEnabled
) {
367 // Swap the list of filtered results so we can freely add new items to the list
368 ResultList curFiltered
;
369 std::swap(curFiltered
, m_filteredOut
);
371 // Filter items already on the list
372 for (int i
= 0; i
< GetItemCount();) {
373 CSearchFile
* file
= (CSearchFile
*)GetItemData(i
);
375 if (IsFiltered(file
)) {
378 m_filteredOut
.push_back(file
);
383 // Check the previously filtered items.
384 ResultList::iterator it
= curFiltered
.begin();
385 for (; it
!= curFiltered
.end(); ++it
) {
386 if (IsFiltered(*it
)) {
389 m_filteredOut
.push_back(*it
);
396 void CSearchListCtrl::EnableFiltering(bool enabled
)
398 if (enabled
!= m_filterEnabled
) {
399 m_filterEnabled
= enabled
;
402 SetFilter(m_filterText
, m_invert
, m_filterKnown
);
404 ResultList::iterator it
= m_filteredOut
.begin();
405 for (; it
!= m_filteredOut
.end(); ++it
) {
409 m_filteredOut
.clear();
415 size_t CSearchListCtrl::GetHiddenItemCount() const
417 return m_filteredOut
.size();
421 bool CSearchListCtrl::IsFiltered(const CSearchFile
* file
)
423 // By default, everything is displayed
426 if (m_filterEnabled
&& m_filter
.IsValid()) {
427 result
= m_filter
.Matches(file
->GetFileName().GetPrintable());
428 result
= ((result
&& !m_invert
) || (!result
&& m_invert
));
430 if (result
&& m_filterKnown
) {
431 result
= !theApp
->downloadqueue
->GetFileByID(file
->GetFileHash());
434 result
= !theApp
->knownfiles
->FindKnownFileByID(file
->GetFileHash());
443 int CSearchListCtrl::SortProc(wxUIntPtr item1
, wxUIntPtr item2
, long sortData
)
445 CSearchFile
* file1
= (CSearchFile
*)item1
;
446 CSearchFile
* file2
= (CSearchFile
*)item2
;
448 // Modifies the result, 1 for ascending, -1 for decending
449 int modifier
= (sortData
& CMuleListCtrl::SORT_DES
) ? -1 : 1;
450 bool alternate
= (sortData
& CMuleListCtrl::SORT_ALT
) != 0;
452 // Decide if which should files we should sort by.
453 wxUIntPtr parent1
= reinterpret_cast<wxUIntPtr
>(file1
->GetParent());
454 wxUIntPtr parent2
= reinterpret_cast<wxUIntPtr
>(file2
->GetParent());
455 wxUIntPtr filePtr1
= reinterpret_cast<wxUIntPtr
>(file1
);
456 wxUIntPtr filePtr2
= reinterpret_cast<wxUIntPtr
>(file2
);
457 if (parent1
&& parent2
) {
458 if (parent1
!= parent2
) {
459 return SortProc(parent1
, parent2
, sortData
);
461 } else if (parent1
) {
462 if (parent1
== filePtr2
) {
465 return SortProc(parent1
, filePtr2
, sortData
);
467 } else if (parent2
) {
468 if (parent2
== filePtr1
) {
471 return SortProc(filePtr1
, parent2
, sortData
);
476 switch (sortData
& CMuleListCtrl::COLUMN_MASK
) {
478 case ID_SEARCH_COL_NAME
:
479 result
= CmpAny(file1
->GetFileName(), file2
->GetFileName());
483 case ID_SEARCH_COL_SIZE
:
484 result
= CmpAny( file1
->GetFileSize(), file2
->GetFileSize() );
488 case ID_SEARCH_COL_SOURCES
: {
489 int cmp
= CmpAny( file1
->GetSourceCount(), file2
->GetSourceCount() );
490 int cmp2
= CmpAny( file1
->GetCompleteSourceCount(), file2
->GetCompleteSourceCount() );
507 // Sort by file-types
508 case ID_SEARCH_COL_TYPE
: {
509 result
= GetFiletypeByName(file1
->GetFileName()).Cmp(GetFiletypeByName(file2
->GetFileName()));
511 // Same file-type, sort by extension
512 result
= CmpAny(file1
->GetFileName().GetExt(), file2
->GetFileName().GetExt());
519 case ID_SEARCH_COL_FILEID
:
520 result
= CmpAny(file2
->GetFileHash(), file1
->GetFileHash());
523 return modifier
* result
;
527 void CSearchListCtrl::SyncLists( CSearchListCtrl
* src
, CSearchListCtrl
* dst
)
529 wxCHECK_RET(src
&& dst
, wxT("NULL argument in SyncLists"));
532 for ( int i
= 0; i
< src
->GetColumnCount(); i
++ ) {
533 // We do this check since just setting the width causes a redraw
534 if ( dst
->GetColumnWidth( i
) != src
->GetColumnWidth( i
) ) {
535 dst
->SetColumnWidth( i
, src
->GetColumnWidth( i
) );
540 unsigned column
= src
->GetSortColumn();
541 unsigned order
= src
->GetSortOrder();
542 if (column
!= dst
->GetSortColumn() || order
!= dst
->GetSortOrder()) {
543 dst
->SetSorting(column
, order
);
548 void CSearchListCtrl::SyncOtherLists( CSearchListCtrl
* src
)
550 std::list
<CSearchListCtrl
*>::iterator it
;
552 for ( it
= s_lists
.begin(); it
!= s_lists
.end(); ++it
) {
553 if ( (*it
) != src
) {
554 SyncLists( src
, *it
);
560 void CSearchListCtrl::OnRightClick(wxListEvent
& event
)
562 CheckSelection(event
);
564 if (GetSelectedItemCount()) {
565 // Create the popup-menu
566 wxMenu
menu(_("File"));
567 menu
.Append(MP_RESUME
, _("Download"));
569 wxMenu
* cats
= new wxMenu(_("Category"));
570 cats
->Append(MP_ASSIGNCAT
, _("Main"));
571 for (unsigned i
= 1; i
< theApp
->glob_prefs
->GetCatCount(); i
++) {
572 cats
->Append(MP_ASSIGNCAT
+ i
,
573 theApp
->glob_prefs
->GetCategory(i
)->title
);
576 menu
.Append(MP_MENU_CATS
, _("Download in category"), cats
);
577 menu
.AppendSeparator();
578 /* Commented out while it's gone
579 menu.Append(MP_RAZORSTATS, _("Get Razorback 2's stats for this file"));
580 menu.AppendSeparator();
582 menu
.Append(MP_SEARCHRELATED
, _("Search related files (ED2k, local server)"));
583 menu
.AppendSeparator();
585 //#warning Uncomment this here to test the MP_MARK_AS_KNOWN feature. Beware! You are on your own here, this might break "known.met"
587 menu
.Append(MP_MARK_AS_KNOWN
, _("Mark as known file"));
588 menu
.AppendSeparator();
591 menu
.Append(MP_GETED2KLINK
, _("Copy ED2k link to clipboard"));
593 // These should only be enabled for single-selections
594 bool enable
= (GetSelectedItemCount() == 1);
595 menu
.Enable(MP_GETED2KLINK
, enable
);
596 menu
.Enable(MP_MENU_CATS
, (theApp
->glob_prefs
->GetCatCount() > 1));
598 PopupMenu(&menu
, event
.GetPoint());
605 void CSearchListCtrl::OnColumnLClick( wxListEvent
& event
)
607 // Let the real event handler do its work first
608 CMuleListCtrl::OnColumnLClick( event
);
610 SyncOtherLists( this );
614 void CSearchListCtrl::OnColumnResize( wxListEvent
& WXUNUSED(event
) )
616 SyncOtherLists( this );
620 void CSearchListCtrl::OnPopupGetUrl( wxCommandEvent
& WXUNUSED(event
) )
624 long index
= GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
626 while( index
!= -1 ) {
627 CSearchFile
* file
= (CSearchFile
*)GetItemData( index
);
629 URIs
+= theApp
->CreateED2kLink( file
) + wxT("\n");
631 index
= GetNextItem( index
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
634 if ( !URIs
.IsEmpty() ) {
635 theApp
->CopyTextToClipboard( URIs
.RemoveLast() );
640 void CSearchListCtrl::OnRazorStatsCheck( wxCommandEvent
& WXUNUSED(event
) )
642 int item
= GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
647 CSearchFile
* file
= (CSearchFile
*)GetItemData( item
);
648 theApp
->amuledlg
->LaunchUrl(wxT("http://stats.razorback2.com/ed2khistory?ed2k=") + file
->GetFileHash().Encode());
652 void CSearchListCtrl::OnRelatedSearch( wxCommandEvent
& WXUNUSED(event
) )
654 int item
= GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
659 CSearchFile
* file
= (CSearchFile
*)GetItemData( item
);
660 theApp
->searchlist
->StopGlobalSearch();
661 theApp
->amuledlg
->m_searchwnd
->ResetControls();
662 CastByID( IDC_SEARCHNAME
, theApp
->amuledlg
->m_searchwnd
, wxTextCtrl
)->
663 SetValue(wxT("related::") + file
->GetFileHash().Encode());
664 theApp
->amuledlg
->m_searchwnd
->StartNewSearch();
668 void CSearchListCtrl::OnMarkAsKnown( wxCommandEvent
& WXUNUSED(event
) )
670 int index
= GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
676 CSearchFile
*searchFile
= (CSearchFile
*)GetItemData(index
);
677 CKnownFile
*knownFile(new CKnownFile(*searchFile
));
678 theApp
->knownfiles
->SafeAddKFile(knownFile
);
679 UpdateAllRelativesColor(searchFile
, index
);
684 void CSearchListCtrl::OnPopupDownload(wxCommandEvent
& event
)
686 if (event
.GetId() == MP_RESUME
) {
687 // Via the "Download" menu-item, use category specified in drop-down menu
690 // Via an "Download in category" item
691 DownloadSelected(event
.GetId() - MP_ASSIGNCAT
);
696 void CSearchListCtrl::OnItemActivated(wxListEvent
& event
)
698 CSearchFile
* file
= ((CSearchFile
*)GetItemData(event
.GetIndex()));
699 if (file
->HasChildren()) {
700 ShowChildren(file
, !file
->ShowChildren());
707 bool CSearchListCtrl::AltSortAllowed(unsigned column
) const
710 case ID_SEARCH_COL_SOURCES
:
719 void CSearchListCtrl::DownloadSelected(int category
)
721 FindWindowById(IDC_SDOWNLOAD
)->Enable(FALSE
);
723 // Either the "Download" menu-item, the download-button, double-click or enter
724 if (category
== -1) {
725 // Defaults to main category
728 if (CastByID(IDC_EXTENDEDSEARCHCHECK
, NULL
, wxCheckBox
)->GetValue()) {
729 category
= CastByID(ID_AUTOCATASSIGN
, NULL
, wxChoice
)->GetSelection();
733 long index
= GetNextItem(-1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
735 CSearchFile
* file
= (CSearchFile
*)GetItemData(index
);
736 CoreNotify_Search_Add_Download(file
, category
);
737 UpdateAllRelativesColor(file
, index
);
738 index
= GetNextItem(index
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
743 const wxBrush
& GetBrush(wxSystemColour index
)
745 return *wxTheBrushList
->FindOrCreateBrush(SYSCOLOR(index
));
749 void CSearchListCtrl::OnDrawItem(
750 int item
, wxDC
* dc
, const wxRect
& rect
, const wxRect
& rectHL
, bool highlighted
)
752 CSearchFile
* file
= (CSearchFile
*)GetItemData(item
);
754 // Define text-color and background
757 dc
->SetBackground(GetBrush(wxSYS_COLOUR_HIGHLIGHT
));
758 dc
->SetTextForeground(SYSCOLOR(wxSYS_COLOUR_HIGHLIGHTTEXT
));
760 dc
->SetBackground(GetBrush(wxSYS_COLOUR_BTNSHADOW
));
761 dc
->SetTextForeground(SYSCOLOR(wxSYS_COLOUR_HIGHLIGHTTEXT
));
764 dc
->SetBackground(GetBrush(wxSYS_COLOUR_LISTBOX
));
765 dc
->SetTextForeground(SYSCOLOR(wxSYS_COLOUR_WINDOWTEXT
));
768 // Define the border of the drawn area
770 dc
->SetPen(wxPen(BLEND(dc
->GetBackground().GetColour(), 65)));
772 dc
->SetPen(*wxTRANSPARENT_PEN
);
773 dc
->SetTextForeground(GetItemTextColour(item
));
776 // Clear the background, not done automatically since the drawing is buffered.
777 dc
->SetBrush( dc
->GetBackground() );
778 dc
->DrawRectangle( rectHL
.x
, rectHL
.y
, rectHL
.width
, rectHL
.height
);
780 // Various constant values we use
781 const int iTextOffset
= ( rect
.GetHeight() - dc
->GetCharHeight() ) / 2;
782 const int iOffset
= 4;
783 const int treeOffset
= 11;
784 const int treeCenter
= 6;
785 bool tree_show
= false;
787 wxRect
cur_rec(iOffset
, rect
.y
, 0, rect
.height
);
788 for (int i
= 0; i
< GetColumnCount(); i
++) {
790 GetColumn(i
, listitem
);
792 if ( listitem
.GetWidth() > 0 ) {
793 cur_rec
.width
= listitem
.GetWidth() - 2*iOffset
;
795 // Make a copy of the current rectangle so we can apply specific tweaks
796 wxRect target_rec
= cur_rec
;
798 // will ensure that text is about in the middle ;)
799 target_rec
.y
+= iTextOffset
;
802 if (file
->HasChildren() || file
->GetParent()) {
803 tree_show
= (listitem
.GetWidth() > 0);
804 target_rec
.x
+= treeOffset
;
805 target_rec
.width
-= treeOffset
;
807 // Children are indented a bit
808 if (file
->GetParent()) {
810 target_rec
.width
-= 4;
814 // Check if the rating icon should be drawn
815 if (file
->HasRating()) {
816 int image
= Client_InvalidRating_Smiley
+ file
->UserRating() - 1;
820 theApp
->amuledlg
->m_imagelist
.Draw(image
, *dc
, target_rec
.GetX(),
821 target_rec
.GetY() - 1, wxIMAGELIST_DRAW_TRANSPARENT
);
823 // Move the text past the icon.
824 target_rec
.x
+= imgWidth
+ 4;
825 target_rec
.width
-= imgWidth
+ 4;
830 cellitem
.SetColumn(i
);
831 cellitem
.SetId(item
);
833 // Force clipper (clip 2 px more than the rectangle from the right side)
834 wxDCClipper
clipper(*dc
, target_rec
.x
, target_rec
.y
, target_rec
.width
- 2, target_rec
.height
);
836 if (GetItem(cellitem
)) {
837 dc
->DrawText(cellitem
.GetText(), target_rec
.GetX(), target_rec
.GetY());
839 dc
->DrawText(wxT("GetItem failed!"), target_rec
.GetX(), target_rec
.GetY());
842 // Increment to the next column
843 cur_rec
.x
+= listitem
.GetWidth();
847 // Draw tree last so it draws over selected and focus (looks better)
849 // Gather some information
850 const bool notLast
= (item
+ 1 < GetItemCount());
851 const bool notFirst
= (item
!= 0);
852 const bool hasNext
= notLast
&& ((CSearchFile
*)GetItemData(item
+ 1))->GetParent();
853 const int middle
= cur_rec
.y
+ ( cur_rec
.height
+ 1 ) / 2;
855 // Set up a new pen for drawing the tree
856 dc
->SetPen( *(wxThePenList
->FindOrCreatePen(dc
->GetTextForeground(), 1, wxSOLID
)) );
858 if (file
->GetParent()) {
859 // Draw the line to the filename
860 dc
->DrawLine(treeCenter
, middle
, treeOffset
+ 4, middle
);
862 // Draw the line to the child node
864 dc
->DrawLine(treeCenter
, middle
, treeCenter
, cur_rec
.y
+ cur_rec
.height
+ 1);
867 // Draw the line back up to parent node
869 dc
->DrawLine(treeCenter
, middle
, treeCenter
, cur_rec
.y
- 1);
871 } else if (file
->HasChildren()) {
872 if (file
->ShowChildren()) {
874 dc
->SetBrush(*wxTRANSPARENT_BRUSH
);
876 dc
->SetBrush(GetItemTextColour(item
));
879 dc
->DrawCircle( treeCenter
, middle
, 3 );
881 // Draw the line to the child node if there are any children
882 if (hasNext
&& file
->ShowChildren()) {
883 dc
->DrawLine(treeCenter
, middle
+ 3, treeCenter
, cur_rec
.y
+ cur_rec
.height
+ 1);
888 // Sanity checks to ensure that results/children are properly positioned.
891 CSearchFile
* parent
= file
->GetParent();
894 CSearchFile
* before
= (CSearchFile
*)GetItemData(item
- 1);
897 wxASSERT((before
->GetParent() == parent
) || (before
== parent
));
899 wxASSERT(before
->GetParent() != file
);
903 if (item
< GetItemCount() - 1) {
904 CSearchFile
* after
= (CSearchFile
*)GetItemData(item
+ 1);
907 wxASSERT((after
->GetParent() == parent
) || (!after
->GetParent()));
909 wxASSERT((after
->GetParent() == file
) || (!after
->GetParent()));
917 void CSearchListCtrl::ShowChildren(CSearchFile
* file
, bool show
)
921 file
->SetShowChildren(show
);
923 const CSearchResultList
& results
= file
->GetChildren();
924 for (size_t i
= 0; i
< results
.size(); ++i
) {
926 AddResult(results
[i
]);
928 RemoveResult(results
[i
]);
936 wxString
CSearchListCtrl::GetTTSText(unsigned item
) const
938 return GetItemText(item
);
940 // File_checked_for_headers