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
28 #include <wx/gauge.h> // Do_not_auto_remove (win32)
30 #include <tags/FileTags.h>
32 #include "SearchDlg.h" // Interface declarations.
33 #include "SearchListCtrl.h" // Needed for CSearchListCtrl
34 #include "muuli_wdr.h" // Needed for IDC_STARTS
35 #include "amuleDlg.h" // Needed for CamuleDlg
36 #include "MuleNotebook.h"
37 #include "GetTickCount.h"
38 #include "Preferences.h"
39 #include "amule.h" // Needed for theApp
40 #include "SearchList.h" // Needed for CSearchList
41 #include <common/Format.h>
44 #define ID_SEARCHLISTCTRL wxID_HIGHEST+667
46 // just to keep compiler happy
47 static wxCommandEvent nullEvent
;
49 BEGIN_EVENT_TABLE(CSearchDlg
, wxPanel
)
50 EVT_BUTTON( IDC_STARTS
, CSearchDlg::OnBnClickedStart
)
51 EVT_TEXT_ENTER( IDC_SEARCHNAME
, CSearchDlg::OnBnClickedStart
)
53 EVT_BUTTON(IDC_CANCELS
, CSearchDlg::OnBnClickedStop
)
55 EVT_LIST_ITEM_SELECTED(ID_SEARCHLISTCTRL
, CSearchDlg::OnListItemSelected
)
57 EVT_BUTTON(IDC_SDOWNLOAD
, CSearchDlg::OnBnClickedDownload
)
58 EVT_BUTTON(IDC_SEARCH_RESET
, CSearchDlg::OnBnClickedReset
)
59 EVT_BUTTON(IDC_CLEAR_RESULTS
, CSearchDlg::OnBnClickedClear
)
61 EVT_CHECKBOX(IDC_EXTENDEDSEARCHCHECK
,CSearchDlg::OnExtendedSearchChange
)
62 EVT_CHECKBOX(IDC_FILTERCHECK
,CSearchDlg::OnFilterCheckChange
)
64 EVT_MULENOTEBOOK_PAGE_CLOSING(ID_NOTEBOOK
, CSearchDlg::OnSearchClosing
)
65 EVT_NOTEBOOK_PAGE_CHANGED(ID_NOTEBOOK
, CSearchDlg::OnSearchPageChanged
)
67 // Event handlers for the parameter fields getting changed
68 EVT_CUSTOM( wxEVT_COMMAND_TEXT_UPDATED
, IDC_SEARCHNAME
, CSearchDlg::OnFieldChanged
)
69 EVT_CUSTOM( wxEVT_COMMAND_TEXT_UPDATED
, IDC_EDITSEARCHEXTENSION
, CSearchDlg::OnFieldChanged
)
70 EVT_CUSTOM( wxEVT_COMMAND_SPINCTRL_UPDATED
, wxID_ANY
, CSearchDlg::OnFieldChanged
)
71 EVT_CUSTOM( wxEVT_COMMAND_CHOICE_SELECTED
, wxID_ANY
, CSearchDlg::OnFieldChanged
)
73 // Event handlers for the filter fields getting changed.
74 EVT_TEXT_ENTER(ID_FILTER_TEXT
, CSearchDlg::OnFilteringChange
)
75 EVT_CHECKBOX(ID_FILTER_INVERT
, CSearchDlg::OnFilteringChange
)
76 EVT_CHECKBOX(ID_FILTER_KNOWN
, CSearchDlg::OnFilteringChange
)
77 EVT_BUTTON(ID_FILTER
, CSearchDlg::OnFilteringChange
)
82 CSearchDlg::CSearchDlg(wxWindow
* pParent
)
83 : wxPanel(pParent
, -1)
85 m_last_search_time
= 0;
87 wxSizer
* content
= searchDlg(this, true);
88 content
->Show(this, true);
90 m_progressbar
= CastChild( ID_SEARCHPROGRESS
, wxGauge
);
91 m_progressbar
->SetRange(100);
93 m_notebook
= CastChild( ID_NOTEBOOK
, CMuleNotebook
);
96 //#warning TODO: restore the image list if/when wxMac supports locating the image
98 // Initialise the image list
99 wxImageList
* m_ImageList
= new wxImageList(16,16);
100 m_ImageList
->Add(amuleSpecial(3));
101 m_ImageList
->Add(amuleSpecial(4));
102 m_notebook
->AssignImageList(m_ImageList
);
106 wxChoice
* searchchoice
= CastChild( ID_SEARCHTYPE
, wxChoice
);
107 wxASSERT(searchchoice
);
108 wxASSERT(searchchoice
->GetString(0) == _("Local"));
109 wxASSERT(searchchoice
->GetString(2) == _("Kad"));
110 wxASSERT(searchchoice
->GetCount() == 4);
112 m_searchchoices
= searchchoice
->GetStrings();
114 // Let's break it now.
118 CastChild( IDC_TypeSearch
, wxChoice
)->SetSelection(0);
119 CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->SetSelection(2);
120 CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->SetSelection(2);
122 // Not there initially.
123 s_searchsizer
->Show(s_extendedsizer
, false);
124 s_searchsizer
->Show(s_filtersizer
, false);
130 CSearchDlg::~CSearchDlg()
134 void CSearchDlg::FixSearchTypes()
136 wxChoice
* searchchoice
= CastChild( ID_SEARCHTYPE
, wxChoice
);
138 searchchoice
->Clear();
140 // We should have only filedonkey now. Let's insert stuff.
144 if (thePrefs::GetNetworkED2K()){
145 searchchoice
->Insert(m_searchchoices
[0], pos
++);
146 searchchoice
->Insert(m_searchchoices
[1], pos
++);
149 if (thePrefs::GetNetworkKademlia()) {
150 searchchoice
->Insert(m_searchchoices
[2], pos
++);
153 searchchoice
->Insert(m_searchchoices
[3], pos
++);
155 searchchoice
->SetSelection(0);
158 CSearchListCtrl
* CSearchDlg::GetSearchList( wxUIntPtr id
)
160 int nPages
= m_notebook
->GetPageCount();
161 for ( int i
= 0; i
< nPages
; i
++ ) {
162 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
164 if (page
->GetSearchId() == id
) {
173 void CSearchDlg::AddResult(CSearchFile
* toadd
)
175 CSearchListCtrl
* outputwnd
= GetSearchList( toadd
->GetSearchID() );
178 outputwnd
->AddResult( toadd
);
180 // Update the result count
181 UpdateHitCount( outputwnd
);
186 void CSearchDlg::UpdateResult(CSearchFile
* toupdate
)
188 CSearchListCtrl
* outputwnd
= GetSearchList( toupdate
->GetSearchID() );
191 outputwnd
->UpdateResult( toupdate
);
193 // Update the result count
194 UpdateHitCount( outputwnd
);
199 void CSearchDlg::OnListItemSelected(wxListEvent
& event
)
201 FindWindow(IDC_SDOWNLOAD
)->Enable(true);
207 void CSearchDlg::OnExtendedSearchChange(wxCommandEvent
& event
)
209 s_searchsizer
->Show(s_extendedsizer
, event
.IsChecked());
215 void CSearchDlg::OnFilterCheckChange(wxCommandEvent
& event
)
217 s_searchsizer
->Show(s_filtersizer
, event
.IsChecked());
220 int nPages
= m_notebook
->GetPageCount();
221 for ( int i
= 0; i
< nPages
; i
++ ) {
222 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
224 page
->EnableFiltering(event
.IsChecked());
226 UpdateHitCount(page
);
231 void CSearchDlg::OnSearchClosing(wxBookCtrlEvent
& evt
)
233 // Abort global search if it was last tab that was closed.
234 if ( evt
.GetSelection() == ((int)m_notebook
->GetPageCount() - 1 ) ) {
235 OnBnClickedStop(nullEvent
);
238 CSearchListCtrl
*ctrl
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(evt
.GetSelection()));
240 // Zero to avoid results added while destructing.
241 ctrl
->ShowResults(0);
242 theApp
->searchlist
->RemoveResults(ctrl
->GetSearchId());
244 // Do cleanups if this was the last tab
245 if ( m_notebook
->GetPageCount() == 1 ) {
246 FindWindow(IDC_SDOWNLOAD
)->Enable(FALSE
);
247 FindWindow(IDC_CLEAR_RESULTS
)->Enable(FALSE
);
252 void CSearchDlg::OnSearchPageChanged(wxBookCtrlEvent
& WXUNUSED(evt
))
254 int selection
= m_notebook
->GetSelection();
256 // Workaround for a bug in wxWidgets, where deletions of pages
257 // can result in an invalid selection. This has been reported as
258 // http://sourceforge.net/tracker/index.php?func=detail&aid=1865141&group_id=9863&atid=109863
259 if (selection
>= (int)m_notebook
->GetPageCount()) {
260 selection
= m_notebook
->GetPageCount() - 1;
263 // Only enable the Download button for pages where files have been selected
264 if ( selection
!= -1 ) {
265 CSearchListCtrl
*ctrl
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(selection
));
267 bool enable
= (ctrl
->GetSelectedItemCount() > 0);
268 FindWindow(IDC_SDOWNLOAD
)->Enable( enable
);
273 void CSearchDlg::OnBnClickedStart(wxCommandEvent
& WXUNUSED(evt
))
275 wxString searchString
= CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue();
276 searchString
.Trim(true);
277 searchString
.Trim(false);
279 if ( searchString
.IsEmpty() ) {
283 wxChoice
* choice
= CastChild( ID_SEARCHTYPE
, wxChoice
);
287 int searchtype
= choice
->GetSelection();
289 if (!thePrefs::GetNetworkED2K()) {
293 if (!thePrefs::GetNetworkKademlia()) {
297 switch ( searchtype
) {
304 // We musn't search more often than once every 2 secs
305 if ((GetTickCount() - m_last_search_time
) > 2000) {
306 m_last_search_time
= GetTickCount();
308 OnBnClickedStop(nullEvent
);
315 // Web Search (FileHash.com)
317 theApp
->amuledlg
->LaunchUrl(theApp
->amuledlg
->GenWebSearchUrl(searchString
, CamuleDlg::WS_FILEHASH
));
327 void CSearchDlg::OnFieldChanged( wxEvent
& WXUNUSED(evt
) )
331 // These are the IDs of the search-fields
332 int textfields
[] = { IDC_SEARCHNAME
, IDC_EDITSEARCHEXTENSION
};
334 for ( uint16 i
= 0; i
< itemsof(textfields
); i
++ ) {
335 enable
|= !CastChild( textfields
[i
], wxTextCtrl
)->GetValue().IsEmpty();
338 // Check if either of the dropdowns have been changed
339 enable
|= (CastChild(IDC_SEARCHMINSIZE
, wxChoice
)->GetSelection() != 2);
340 enable
|= (CastChild(IDC_SEARCHMAXSIZE
, wxChoice
)->GetSelection() != 2);
341 enable
|= (CastChild(IDC_TypeSearch
, wxChoice
)->GetSelection() > 0);
342 enable
|= (CastChild(ID_AUTOCATASSIGN
, wxChoice
)->GetSelection() > 0);
344 // These are the IDs of the search-fields
345 int spinfields
[] = { IDC_SPINSEARCHMIN
, IDC_SPINSEARCHMAX
, IDC_SPINSEARCHAVAIBILITY
};
346 for ( uint16 i
= 0; i
< itemsof(spinfields
); i
++ ) {
347 enable
|= (CastChild( spinfields
[i
], wxSpinCtrl
)->GetValue() > 0);
350 // Enable the "Reset" button if any fields contain text
351 FindWindow(IDC_SEARCH_RESET
)->Enable( enable
);
353 // Enable the Server Search button if the Name field contains text
354 enable
= !CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue().IsEmpty();
355 FindWindow(IDC_STARTS
)->Enable( enable
);
359 void CSearchDlg::OnFilteringChange(wxCommandEvent
& WXUNUSED(evt
))
361 wxString filter
= CastChild(ID_FILTER_TEXT
, wxTextCtrl
)->GetValue();
362 bool invert
= CastChild(ID_FILTER_INVERT
, wxCheckBox
)->GetValue();
363 bool known
= CastChild(ID_FILTER_KNOWN
, wxCheckBox
)->GetValue();
365 // Check that the expression compiles before we try to assign it
366 // Otherwise we will get an error-dialog for each result-list.
367 if (wxRegEx(filter
, wxRE_DEFAULT
| wxRE_ICASE
).IsValid()) {
368 int nPages
= m_notebook
->GetPageCount();
369 for ( int i
= 0; i
< nPages
; i
++ ) {
370 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
372 page
->SetFilter(filter
, invert
, known
);
374 UpdateHitCount(page
);
380 bool CSearchDlg::CheckTabNameExists(const wxString
& searchString
)
382 int nPages
= m_notebook
->GetPageCount();
383 for ( int i
= 0; i
< nPages
; i
++ ) {
384 // The BeforeLast(' ') is to strip the hit-count from the name
385 if ( m_notebook
->GetPageText(i
).BeforeLast(wxT(' ')) == searchString
) {
394 void CSearchDlg::CreateNewTab(const wxString
& searchString
, wxUIntPtr nSearchID
)
396 CSearchListCtrl
* list
= new CSearchListCtrl(m_notebook
, ID_SEARCHLISTCTRL
, wxDefaultPosition
, wxDefaultSize
, wxLC_REPORT
|wxNO_BORDER
);
397 m_notebook
->AddPage(list
, searchString
, true, 0);
399 // Ensure that new results are filtered
400 bool enable
= CastChild(IDC_FILTERCHECK
, wxCheckBox
)->GetValue();
401 wxString filter
= CastChild(ID_FILTER_TEXT
, wxTextCtrl
)->GetValue();
402 bool invert
= CastChild(ID_FILTER_INVERT
, wxCheckBox
)->GetValue();
403 bool known
= CastChild(ID_FILTER_KNOWN
, wxCheckBox
)->GetValue();
405 list
->SetFilter(filter
, invert
, known
);
406 list
->EnableFiltering(enable
);
407 list
->ShowResults(nSearchID
);
410 FindWindow(IDC_CLEAR_RESULTS
)->Enable(true);
414 void CSearchDlg::OnBnClickedStop(wxCommandEvent
& WXUNUSED(evt
))
416 theApp
->searchlist
->StopSearch();
421 void CSearchDlg::ResetControls()
423 m_progressbar
->SetValue(0);
425 FindWindow(IDC_CANCELS
)->Disable();
426 FindWindow(IDC_STARTS
)->Enable(!CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue().IsEmpty());
430 void CSearchDlg::LocalSearchEnd()
435 void CSearchDlg::KadSearchEnd(uint32 id
)
437 int nPages
= m_notebook
->GetPageCount();
438 for (int i
= 0; i
< nPages
; ++i
) {
439 CSearchListCtrl
* page
=
440 dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
441 if (page
->GetSearchId() == id
|| id
== 0) { // 0: just update all pages (there is only one KAD search running at a time anyway)
443 if (m_notebook
->GetPageText(i
).StartsWith(wxT("!"),&rest
)) {
444 m_notebook
->SetPageText(i
,rest
);
450 void CSearchDlg::OnBnClickedDownload(wxCommandEvent
& WXUNUSED(evt
))
452 int sel
= m_notebook
->GetSelection();
454 CSearchListCtrl
* list
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(sel
));
456 // Download with items added to category specified in the drop-down menu
457 list
->DownloadSelected();
462 void CSearchDlg::OnBnClickedClear(wxCommandEvent
& WXUNUSED(ev
))
464 OnBnClickedStop(nullEvent
);
466 m_notebook
->DeleteAllPages();
468 FindWindow(IDC_CLEAR_RESULTS
)->Enable(FALSE
);
469 FindWindow(IDC_SDOWNLOAD
)->Enable(FALSE
);
473 void CSearchDlg::StartNewSearch()
475 static uint32 m_nSearchID
= 0;
478 FindWindow(IDC_STARTS
)->Disable();
479 FindWindow(IDC_SDOWNLOAD
)->Disable();
480 FindWindow(IDC_CANCELS
)->Enable();
482 CSearchList::CSearchParams params
;
484 params
.searchString
= CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue();
485 params
.searchString
.Trim(true);
486 params
.searchString
.Trim(false);
488 if (params
.searchString
.IsEmpty()) {
492 if (CastChild(IDC_EXTENDEDSEARCHCHECK
, wxCheckBox
)->GetValue()) {
493 params
.extension
= CastChild( IDC_EDITSEARCHEXTENSION
, wxTextCtrl
)->GetValue();
495 uint32 sizemin
= GetTypeSize( (uint8
) CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->GetSelection() );
496 uint32 sizemax
= GetTypeSize( (uint8
) CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->GetSelection() );
498 // Parameter Minimum Size
499 params
.minSize
= (uint64_t)(CastChild( IDC_SPINSEARCHMIN
, wxSpinCtrl
)->GetValue()) * (uint64_t)sizemin
;
501 // Parameter Maximum Size
502 params
.maxSize
= (uint64_t)(CastChild( IDC_SPINSEARCHMAX
, wxSpinCtrl
)->GetValue()) * (uint64_t)sizemax
;
504 if ((params
.maxSize
< params
.minSize
) && (params
.maxSize
)) {
505 wxMessageDialog
dlg(this,
506 _("Min size must be smaller than max size. Max size ignored."),
507 _("Search warning"), wxOK
|wxCENTRE
|wxICON_INFORMATION
);
513 // Parameter Availability
514 params
.availability
= CastChild( IDC_SPINSEARCHAVAIBILITY
, wxSpinCtrl
)->GetValue();
516 switch ( CastChild( IDC_TypeSearch
, wxChoice
)->GetSelection() ) {
517 case 0: params
.typeText
.Clear(); break;
518 case 1: params
.typeText
= ED2KFTSTR_ARCHIVE
; break;
519 case 2: params
.typeText
= ED2KFTSTR_AUDIO
; break;
520 case 3: params
.typeText
= ED2KFTSTR_CDIMAGE
; break;
521 case 4: params
.typeText
= ED2KFTSTR_IMAGE
; break;
522 case 5: params
.typeText
= ED2KFTSTR_PROGRAM
; break;
523 case 6: params
.typeText
= ED2KFTSTR_DOCUMENT
; break;
524 case 7: params
.typeText
= ED2KFTSTR_VIDEO
; break;
526 AddDebugLogLineC( logGeneral
,
527 CFormat( wxT("Warning! Unknown search-category (%s) selected!") )
534 SearchType search_type
= KadSearch
;
536 int selection
= CastChild( ID_SEARCHTYPE
, wxChoice
)->GetSelection();
538 if (!thePrefs::GetNetworkED2K()) {
542 if (!thePrefs::GetNetworkKademlia()) {
547 case 0: // Local Search
548 search_type
= LocalSearch
;
550 case 1: // Global Search
551 search_type
= GlobalSearch
;
553 case 2: // Kad search
554 search_type
= KadSearch
;
557 // Should never happen
562 uint32 real_id
= m_nSearchID
;
563 wxString error
= theApp
->searchlist
->StartNewSearch(&real_id
, search_type
, params
);
564 if (!error
.IsEmpty()) {
565 // Search failed / Remote in progress
566 wxMessageBox(error
, _("Search warning"),
567 wxOK
| wxCENTRE
| wxICON_INFORMATION
, this);
568 FindWindow(IDC_STARTS
)->Enable();
569 FindWindow(IDC_SDOWNLOAD
)->Disable();
570 FindWindow(IDC_CANCELS
)->Disable();
573 ((search_type
== KadSearch
) ? wxT("!") : wxEmptyString
) +
574 params
.searchString
+ wxT(" (0)"),
580 void CSearchDlg::UpdateHitCount(CSearchListCtrl
* page
)
582 for ( uint32 i
= 0; i
< (uint32
)m_notebook
->GetPageCount(); ++i
) {
583 if ( m_notebook
->GetPage(i
) == page
) {
584 wxString searchtxt
= m_notebook
->GetPageText(i
).BeforeLast(wxT(' '));
586 if ( !searchtxt
.IsEmpty() ) {
587 size_t shown
= page
->GetItemCount();
588 size_t hidden
= page
->GetHiddenItemCount();
591 searchtxt
+= CFormat(wxT(" (%u/%u)")) % shown
% (shown
+ hidden
);
593 searchtxt
+= CFormat(wxT(" (%u)")) % shown
;
596 m_notebook
->SetPageText(i
, searchtxt
);
605 void CSearchDlg::OnBnClickedReset(wxCommandEvent
& WXUNUSED(evt
))
607 CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->Clear();
608 CastChild( IDC_EDITSEARCHEXTENSION
, wxTextCtrl
)->Clear();
609 CastChild( IDC_SPINSEARCHMIN
, wxSpinCtrl
)->SetValue(0);
610 CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->SetSelection(2);
611 CastChild( IDC_SPINSEARCHMAX
, wxSpinCtrl
)->SetValue(0);
612 CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->SetSelection(2);
613 CastChild( IDC_SPINSEARCHAVAIBILITY
, wxSpinCtrl
)->SetValue(0);
614 CastChild( IDC_TypeSearch
, wxChoice
)->SetSelection(0);
615 CastChild( ID_AUTOCATASSIGN
, wxChoice
)->SetSelection(0);
617 FindWindow(IDC_SEARCH_RESET
)->Enable(FALSE
);
621 void CSearchDlg::UpdateCatChoice()
623 wxChoice
* c_cat
= CastChild( ID_AUTOCATASSIGN
, wxChoice
);
626 c_cat
->Append(_("Main"));
628 for ( unsigned i
= 1; i
< theApp
->glob_prefs
->GetCatCount(); i
++ ) {
629 c_cat
->Append( theApp
->glob_prefs
->GetCategory( i
)->title
);
632 c_cat
->SetSelection( 0 );
635 void CSearchDlg::UpdateProgress(uint32 new_value
) {
636 m_progressbar
->SetValue(new_value
);
638 // File_checked_for_headers