2 // This file is part of the aMule Project.
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 )
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 "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
)
56 std::list
<CSearchListCtrl
*> CSearchListCtrl::s_lists
;
59 enum SearchListColumns
{
60 ID_SEARCH_COL_NAME
= 0,
62 ID_SEARCH_COL_SOURCES
,
66 ID_SEARCH_COL_DIRECTORY
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, 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
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") );
104 // Unset the name to avoid the settings getting saved every time a list is closed
105 SetTableName( wxEmptyString
);
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() )
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) {
147 } else if (parent
&& !parent
->ShowChildren()) {
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
))) {
162 // No children left, and the parent is filtered.
163 m_filteredOut
.push_back(toshow
);
167 m_filteredOut
.push_back(toshow
);
172 // Insert the item before the item found by the search
175 insertPos
= FindItem(-1, (wxUIntPtr
)parent
);
176 if (insertPos
== -1) {
178 insertPos
= GetItemCount();
183 insertPos
= GetInsertPos(toshowdata
);
185 long newid
= InsertItem(insertPos
, toshow
->GetFileName().GetPrintable());
187 // Sanity checks to ensure that results/children are properly positioned.
191 CSearchFile
* before
= reinterpret_cast<CSearchFile
*>(GetItemData(newid
- 1));
194 wxASSERT((before
->GetParent() == parent
) || (before
== parent
));
196 wxASSERT(before
->GetParent() != toshow
);
200 if ((int)newid
< GetItemCount() - 1) {
201 CSearchFile
* after
= reinterpret_cast<CSearchFile
*>(GetItemData(newid
+ 1));
204 wxASSERT((after
->GetParent() == parent
) || (!after
->GetParent()));
206 wxASSERT((after
->GetParent() == toshow
) || (!after
->GetParent()));
212 SetItemPtrData(newid
, toshowdata
);
215 SetItem(newid
, ID_SEARCH_COL_SIZE
, CastItoXBytes( toshow
->GetFileSize() ) );
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) {
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);
235 SetItem( newid
, ID_SEARCH_COL_SOURCES
, temp
);
238 SetItem( newid
, ID_SEARCH_COL_TYPE
, GetFiletypeByName( toshow
->GetFileName() ) );
241 SetItem(newid
, ID_SEARCH_COL_FILEID
, toshow
->GetFileHash().Encode() );
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
));
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
));
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) {
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);
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
)) {
309 void CSearchListCtrl::UpdateItemColor(long index
)
313 item
.SetColumn( ID_SEARCH_COL_SIZE
);
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.
336 case CSearchFile::QUEUED
:
337 // File is downloading.
338 case CSearchFile::QUEUEDCANCELED
:
339 // File is downloading and has been canceled before.
343 case CSearchFile::CANCELED
:
344 // File has been canceled. Mark as magenta.
349 // File is new, colour after number of files
350 blue
+= file
->GetSourceCount() * 5;
356 // don't forget to set the item data back...
358 newitem
.SetId( index
);
359 newitem
.SetTextColour( wxColour( red
, green
, blue
) );
365 void CSearchListCtrl::ShowResults( long ResultsID
)
368 m_nResultsID
= 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()
384 void CSearchListCtrl::SetFilter(const wxString
& regExp
, bool invert
, bool filterKnown
)
386 if (regExp
.IsEmpty()) {
388 m_filterText
= wxT(".*");
390 m_filterText
= regExp
;
393 m_filter
.Compile(m_filterText
, wxRE_DEFAULT
| wxRE_ICASE
);
394 m_filterKnown
= filterKnown
;
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
)) {
409 m_filteredOut
.push_back(file
);
414 // Check the previously filtered items.
415 ResultList::iterator it
= curFiltered
.begin();
416 for (; it
!= curFiltered
.end(); ++it
) {
417 if (IsFiltered(*it
)) {
420 m_filteredOut
.push_back(*it
);
427 void CSearchListCtrl::EnableFiltering(bool enabled
)
429 if (enabled
!= m_filterEnabled
) {
430 m_filterEnabled
= enabled
;
433 SetFilter(m_filterText
, m_invert
, m_filterKnown
);
435 ResultList::iterator it
= m_filteredOut
.begin();
436 for (; it
!= m_filteredOut
.end(); ++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
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
;
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
) {
491 return SortProc(parent1
, filePtr2
, sortData
);
493 } else if (parent2
) {
494 if (parent2
== filePtr1
) {
497 return SortProc(filePtr1
, parent2
, sortData
);
502 switch (sortData
& CMuleListCtrl::COLUMN_MASK
) {
504 case ID_SEARCH_COL_NAME
:
505 result
= CmpAny(file1
->GetFileName(), file2
->GetFileName());
509 case ID_SEARCH_COL_SIZE
:
510 result
= CmpAny( file1
->GetFileSize(), file2
->GetFileSize() );
514 case ID_SEARCH_COL_SOURCES
: {
515 int cmp
= CmpAny( file1
->GetSourceCount(), file2
->GetSourceCount() );
516 int cmp2
= CmpAny( file1
->GetCompleteSourceCount(), file2
->GetCompleteSourceCount() );
533 // Sort by file-types
534 case ID_SEARCH_COL_TYPE
: {
535 result
= GetFiletypeByName(file1
->GetFileName()).Cmp(GetFiletypeByName(file2
->GetFileName()));
537 // Same file-type, sort by extension
538 result
= CmpAny(file1
->GetFileName().GetExt(), file2
->GetFileName().GetExt());
545 case ID_SEARCH_COL_FILEID
:
546 result
= CmpAny(file2
->GetFileHash(), file1
->GetFileHash());
549 // Sort by file status
550 case ID_SEARCH_COL_STATUS
:
551 result
= CmpAny(DetermineStatusPrintable(file2
), DetermineStatusPrintable(file1
));
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());
563 return modifier
* result
;
567 void CSearchListCtrl::SetSorting(unsigned column
, unsigned order
)
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
);
585 void CSearchListCtrl::SyncLists( CSearchListCtrl
* src
, CSearchListCtrl
* dst
)
587 wxCHECK_RET(src
&& dst
, wxT("NULL argument in SyncLists"));
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
) );
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
) {
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"
648 menu
.Append(MP_MARK_AS_KNOWN
, _("Mark as known file"));
649 menu
.AppendSeparator();
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());
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
) )
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
);
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
);
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
) )
732 long index
= GetNextItem(-1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
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
);
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
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());
766 bool CSearchListCtrl::AltSortAllowed(unsigned column
) const
769 case ID_SEARCH_COL_SOURCES
:
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
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
);
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 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
816 dc
->SetBackground(GetBrush(wxSYS_COLOUR_HIGHLIGHT
));
817 dc
->SetTextForeground(CMuleColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
819 dc
->SetBackground(GetBrush(wxSYS_COLOUR_BTNSHADOW
));
820 dc
->SetTextForeground(CMuleColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
823 dc
->SetBackground(GetBrush(wxSYS_COLOUR_LISTBOX
));
824 dc
->SetTextForeground(CMuleColour(wxSYS_COLOUR_WINDOWTEXT
));
827 // Define the border of the drawn area
829 dc
->SetPen(*(wxThePenList
->FindOrCreatePen(CMuleColour(dc
->GetBackground().GetColour()).Blend(65), 1, wxSOLID
)));
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
++) {
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
;
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()) {
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;
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;
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());
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)
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
923 dc
->DrawLine(treeCenter
, middle
, treeCenter
, cur_rec
.y
+ cur_rec
.height
+ 1);
926 // Draw the line back up to parent node
928 dc
->DrawLine(treeCenter
, middle
, treeCenter
, cur_rec
.y
- 1);
930 } else if (file
->HasChildren()) {
931 if (file
->ShowChildren()) {
933 dc
->SetBrush(*wxTRANSPARENT_BRUSH
);
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.
950 CSearchFile
* parent
= file
->GetParent();
953 CSearchFile
* before
= reinterpret_cast<CSearchFile
*>(GetItemData(item
- 1));
956 wxASSERT((before
->GetParent() == parent
) || (before
== parent
));
958 wxASSERT(before
->GetParent() != file
);
962 if (item
< GetItemCount() - 1) {
963 CSearchFile
* after
= reinterpret_cast<CSearchFile
*>(GetItemData(item
+ 1));
966 wxASSERT((after
->GetParent() == parent
) || (!after
->GetParent()));
968 wxASSERT((after
->GetParent() == file
) || (!after
->GetParent()));
976 void CSearchListCtrl::ShowChildren(CSearchFile
* file
, bool show
)
980 file
->SetShowChildren(show
);
982 const CSearchResultList
& results
= file
->GetChildren();
983 for (size_t i
= 0; i
< results
.size(); ++i
) {
985 AddResult(results
[i
]);
987 RemoveResult(results
[i
]);
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.
1012 case CSearchFile::CANCELED
:
1013 // File has been canceled.
1014 return _("Canceled");
1020 // File_checked_for_headers