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() == 3);
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
->SetSelection(0);
156 CSearchListCtrl
* CSearchDlg::GetSearchList( wxUIntPtr id
)
158 int nPages
= m_notebook
->GetPageCount();
159 for ( int i
= 0; i
< nPages
; i
++ ) {
160 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
162 if (page
->GetSearchId() == id
) {
171 void CSearchDlg::AddResult(CSearchFile
* toadd
)
173 CSearchListCtrl
* outputwnd
= GetSearchList( toadd
->GetSearchID() );
176 outputwnd
->AddResult( toadd
);
178 // Update the result count
179 UpdateHitCount( outputwnd
);
184 void CSearchDlg::UpdateResult(CSearchFile
* toupdate
)
186 CSearchListCtrl
* outputwnd
= GetSearchList( toupdate
->GetSearchID() );
189 outputwnd
->UpdateResult( toupdate
);
191 // Update the result count
192 UpdateHitCount( outputwnd
);
197 void CSearchDlg::OnListItemSelected(wxListEvent
& event
)
199 FindWindow(IDC_SDOWNLOAD
)->Enable(true);
205 void CSearchDlg::OnExtendedSearchChange(wxCommandEvent
& event
)
207 s_searchsizer
->Show(s_extendedsizer
, event
.IsChecked());
213 void CSearchDlg::OnFilterCheckChange(wxCommandEvent
& event
)
215 s_searchsizer
->Show(s_filtersizer
, event
.IsChecked());
218 int nPages
= m_notebook
->GetPageCount();
219 for ( int i
= 0; i
< nPages
; i
++ ) {
220 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
222 page
->EnableFiltering(event
.IsChecked());
224 UpdateHitCount(page
);
229 void CSearchDlg::OnSearchClosing(wxBookCtrlEvent
& evt
)
231 // Abort global search if it was last tab that was closed.
232 if ( evt
.GetSelection() == ((int)m_notebook
->GetPageCount() - 1 ) ) {
233 OnBnClickedStop(nullEvent
);
236 CSearchListCtrl
*ctrl
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(evt
.GetSelection()));
238 // Zero to avoid results added while destructing.
239 ctrl
->ShowResults(0);
240 theApp
->searchlist
->RemoveResults(ctrl
->GetSearchId());
242 // Do cleanups if this was the last tab
243 if ( m_notebook
->GetPageCount() == 1 ) {
244 FindWindow(IDC_SDOWNLOAD
)->Enable(FALSE
);
245 FindWindow(IDC_CLEAR_RESULTS
)->Enable(FALSE
);
250 void CSearchDlg::OnSearchPageChanged(wxBookCtrlEvent
& WXUNUSED(evt
))
252 int selection
= m_notebook
->GetSelection();
254 // Workaround for a bug in wxWidgets, where deletions of pages
255 // can result in an invalid selection. This has been reported as
256 // http://sourceforge.net/tracker/index.php?func=detail&aid=1865141&group_id=9863&atid=109863
257 if (selection
>= (int)m_notebook
->GetPageCount()) {
258 selection
= m_notebook
->GetPageCount() - 1;
261 // Only enable the Download button for pages where files have been selected
262 if ( selection
!= -1 ) {
263 CSearchListCtrl
*ctrl
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(selection
));
265 bool enable
= (ctrl
->GetSelectedItemCount() > 0);
266 FindWindow(IDC_SDOWNLOAD
)->Enable( enable
);
271 void CSearchDlg::OnBnClickedStart(wxCommandEvent
& WXUNUSED(evt
))
273 if (!thePrefs::GetNetworkED2K() && !thePrefs::GetNetworkKademlia()) {
274 wxMessageBox(_("It's impossible to search when both eD2k and Kademlia are disabled."),
276 wxOK
|wxCENTRE
|wxICON_ERROR
281 // We mustn't search more often than once every 2 secs
282 if ((GetTickCount() - m_last_search_time
) > 2000) {
283 m_last_search_time
= GetTickCount();
284 OnBnClickedStop(nullEvent
);
290 void CSearchDlg::OnFieldChanged( wxEvent
& WXUNUSED(evt
) )
294 // These are the IDs of the search-fields
295 int textfields
[] = { IDC_SEARCHNAME
, IDC_EDITSEARCHEXTENSION
};
297 for ( uint16 i
= 0; i
< itemsof(textfields
); i
++ ) {
298 enable
|= !CastChild( textfields
[i
], wxTextCtrl
)->GetValue().IsEmpty();
301 // Check if either of the dropdowns have been changed
302 enable
|= (CastChild(IDC_SEARCHMINSIZE
, wxChoice
)->GetSelection() != 2);
303 enable
|= (CastChild(IDC_SEARCHMAXSIZE
, wxChoice
)->GetSelection() != 2);
304 enable
|= (CastChild(IDC_TypeSearch
, wxChoice
)->GetSelection() > 0);
305 enable
|= (CastChild(ID_AUTOCATASSIGN
, wxChoice
)->GetSelection() > 0);
307 // These are the IDs of the search-fields
308 int spinfields
[] = { IDC_SPINSEARCHMIN
, IDC_SPINSEARCHMAX
, IDC_SPINSEARCHAVAIBILITY
};
309 for ( uint16 i
= 0; i
< itemsof(spinfields
); i
++ ) {
310 enable
|= (CastChild( spinfields
[i
], wxSpinCtrl
)->GetValue() > 0);
313 // Enable the "Reset" button if any fields contain text
314 FindWindow(IDC_SEARCH_RESET
)->Enable( enable
);
316 // Enable the Server Search button if the Name field contains text
317 enable
= !CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue().IsEmpty();
318 FindWindow(IDC_STARTS
)->Enable( enable
);
322 void CSearchDlg::OnFilteringChange(wxCommandEvent
& WXUNUSED(evt
))
324 wxString filter
= CastChild(ID_FILTER_TEXT
, wxTextCtrl
)->GetValue();
325 bool invert
= CastChild(ID_FILTER_INVERT
, wxCheckBox
)->GetValue();
326 bool known
= CastChild(ID_FILTER_KNOWN
, wxCheckBox
)->GetValue();
328 // Check that the expression compiles before we try to assign it
329 // Otherwise we will get an error-dialog for each result-list.
330 if (wxRegEx(filter
, wxRE_DEFAULT
| wxRE_ICASE
).IsValid()) {
331 int nPages
= m_notebook
->GetPageCount();
332 for ( int i
= 0; i
< nPages
; i
++ ) {
333 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
335 page
->SetFilter(filter
, invert
, known
);
337 UpdateHitCount(page
);
343 bool CSearchDlg::CheckTabNameExists(const wxString
& searchString
)
345 int nPages
= m_notebook
->GetPageCount();
346 for ( int i
= 0; i
< nPages
; i
++ ) {
347 // The BeforeLast(' ') is to strip the hit-count from the name
348 if ( m_notebook
->GetPageText(i
).BeforeLast(wxT(' ')) == searchString
) {
357 void CSearchDlg::CreateNewTab(const wxString
& searchString
, wxUIntPtr nSearchID
)
359 CSearchListCtrl
* list
= new CSearchListCtrl(m_notebook
, ID_SEARCHLISTCTRL
, wxDefaultPosition
, wxDefaultSize
, wxLC_REPORT
|wxNO_BORDER
);
360 m_notebook
->AddPage(list
, searchString
, true, 0);
362 // Ensure that new results are filtered
363 bool enable
= CastChild(IDC_FILTERCHECK
, wxCheckBox
)->GetValue();
364 wxString filter
= CastChild(ID_FILTER_TEXT
, wxTextCtrl
)->GetValue();
365 bool invert
= CastChild(ID_FILTER_INVERT
, wxCheckBox
)->GetValue();
366 bool known
= CastChild(ID_FILTER_KNOWN
, wxCheckBox
)->GetValue();
368 list
->SetFilter(filter
, invert
, known
);
369 list
->EnableFiltering(enable
);
370 list
->ShowResults(nSearchID
);
373 FindWindow(IDC_CLEAR_RESULTS
)->Enable(true);
377 void CSearchDlg::OnBnClickedStop(wxCommandEvent
& WXUNUSED(evt
))
379 theApp
->searchlist
->StopSearch();
384 void CSearchDlg::ResetControls()
386 m_progressbar
->SetValue(0);
388 FindWindow(IDC_CANCELS
)->Disable();
389 FindWindow(IDC_STARTS
)->Enable(!CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue().IsEmpty());
393 void CSearchDlg::LocalSearchEnd()
398 void CSearchDlg::KadSearchEnd(uint32 id
)
400 int nPages
= m_notebook
->GetPageCount();
401 for (int i
= 0; i
< nPages
; ++i
) {
402 CSearchListCtrl
* page
=
403 dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
404 if (page
->GetSearchId() == id
|| id
== 0) { // 0: just update all pages (there is only one KAD search running at a time anyway)
406 if (m_notebook
->GetPageText(i
).StartsWith(wxT("!"),&rest
)) {
407 m_notebook
->SetPageText(i
,rest
);
413 void CSearchDlg::OnBnClickedDownload(wxCommandEvent
& WXUNUSED(evt
))
415 int sel
= m_notebook
->GetSelection();
417 CSearchListCtrl
* list
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(sel
));
419 // Download with items added to category specified in the drop-down menu
420 list
->DownloadSelected();
425 void CSearchDlg::OnBnClickedClear(wxCommandEvent
& WXUNUSED(ev
))
427 OnBnClickedStop(nullEvent
);
429 m_notebook
->DeleteAllPages();
431 FindWindow(IDC_CLEAR_RESULTS
)->Enable(FALSE
);
432 FindWindow(IDC_SDOWNLOAD
)->Enable(FALSE
);
436 void CSearchDlg::StartNewSearch()
438 static uint32 m_nSearchID
= 0;
441 FindWindow(IDC_STARTS
)->Disable();
442 FindWindow(IDC_SDOWNLOAD
)->Disable();
443 FindWindow(IDC_CANCELS
)->Enable();
445 CSearchList::CSearchParams params
;
447 params
.searchString
= CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue();
448 params
.searchString
.Trim(true);
449 params
.searchString
.Trim(false);
451 if (params
.searchString
.IsEmpty()) {
455 if (CastChild(IDC_EXTENDEDSEARCHCHECK
, wxCheckBox
)->GetValue()) {
456 params
.extension
= CastChild( IDC_EDITSEARCHEXTENSION
, wxTextCtrl
)->GetValue();
458 uint32 sizemin
= GetTypeSize( (uint8
) CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->GetSelection() );
459 uint32 sizemax
= GetTypeSize( (uint8
) CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->GetSelection() );
461 // Parameter Minimum Size
462 params
.minSize
= (uint64_t)(CastChild( IDC_SPINSEARCHMIN
, wxSpinCtrl
)->GetValue()) * (uint64_t)sizemin
;
464 // Parameter Maximum Size
465 params
.maxSize
= (uint64_t)(CastChild( IDC_SPINSEARCHMAX
, wxSpinCtrl
)->GetValue()) * (uint64_t)sizemax
;
467 if ((params
.maxSize
< params
.minSize
) && (params
.maxSize
)) {
468 wxMessageDialog
dlg(this,
469 _("Min size must be smaller than max size. Max size ignored."),
470 _("Search warning"), wxOK
|wxCENTRE
|wxICON_INFORMATION
);
476 // Parameter Availability
477 params
.availability
= CastChild( IDC_SPINSEARCHAVAIBILITY
, wxSpinCtrl
)->GetValue();
479 switch ( CastChild( IDC_TypeSearch
, wxChoice
)->GetSelection() ) {
480 case 0: params
.typeText
.Clear(); break;
481 case 1: params
.typeText
= ED2KFTSTR_ARCHIVE
; break;
482 case 2: params
.typeText
= ED2KFTSTR_AUDIO
; break;
483 case 3: params
.typeText
= ED2KFTSTR_CDIMAGE
; break;
484 case 4: params
.typeText
= ED2KFTSTR_IMAGE
; break;
485 case 5: params
.typeText
= ED2KFTSTR_PROGRAM
; break;
486 case 6: params
.typeText
= ED2KFTSTR_DOCUMENT
; break;
487 case 7: params
.typeText
= ED2KFTSTR_VIDEO
; break;
489 AddDebugLogLineC( logGeneral
,
490 CFormat( wxT("Warning! Unknown search-category (%s) selected!") )
497 SearchType search_type
= KadSearch
;
499 int selection
= CastChild( ID_SEARCHTYPE
, wxChoice
)->GetSelection();
501 if (!thePrefs::GetNetworkED2K()) {
505 if (!thePrefs::GetNetworkKademlia()) {
510 case 0: // Local Search
511 search_type
= LocalSearch
;
513 case 1: // Global Search
514 search_type
= GlobalSearch
;
516 case 2: // Kad search
517 search_type
= KadSearch
;
520 // Should never happen
525 uint32 real_id
= m_nSearchID
;
526 wxString error
= theApp
->searchlist
->StartNewSearch(&real_id
, search_type
, params
);
527 if (!error
.IsEmpty()) {
528 // Search failed / Remote in progress
529 wxMessageBox(error
, _("Search warning"),
530 wxOK
| wxCENTRE
| wxICON_INFORMATION
, this);
531 FindWindow(IDC_STARTS
)->Enable();
532 FindWindow(IDC_SDOWNLOAD
)->Disable();
533 FindWindow(IDC_CANCELS
)->Disable();
536 ((search_type
== KadSearch
) ? wxT("!") : wxEmptyString
) +
537 params
.searchString
+ wxT(" (0)"),
543 void CSearchDlg::UpdateHitCount(CSearchListCtrl
* page
)
545 for ( uint32 i
= 0; i
< (uint32
)m_notebook
->GetPageCount(); ++i
) {
546 if ( m_notebook
->GetPage(i
) == page
) {
547 wxString searchtxt
= m_notebook
->GetPageText(i
).BeforeLast(wxT(' '));
549 if ( !searchtxt
.IsEmpty() ) {
550 size_t shown
= page
->GetItemCount();
551 size_t hidden
= page
->GetHiddenItemCount();
554 searchtxt
+= CFormat(wxT(" (%u/%u)")) % shown
% (shown
+ hidden
);
556 searchtxt
+= CFormat(wxT(" (%u)")) % shown
;
559 m_notebook
->SetPageText(i
, searchtxt
);
568 void CSearchDlg::OnBnClickedReset(wxCommandEvent
& WXUNUSED(evt
))
570 CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->Clear();
571 CastChild( IDC_EDITSEARCHEXTENSION
, wxTextCtrl
)->Clear();
572 CastChild( IDC_SPINSEARCHMIN
, wxSpinCtrl
)->SetValue(0);
573 CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->SetSelection(2);
574 CastChild( IDC_SPINSEARCHMAX
, wxSpinCtrl
)->SetValue(0);
575 CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->SetSelection(2);
576 CastChild( IDC_SPINSEARCHAVAIBILITY
, wxSpinCtrl
)->SetValue(0);
577 CastChild( IDC_TypeSearch
, wxChoice
)->SetSelection(0);
578 CastChild( ID_AUTOCATASSIGN
, wxChoice
)->SetSelection(0);
580 FindWindow(IDC_SEARCH_RESET
)->Enable(FALSE
);
584 void CSearchDlg::UpdateCatChoice()
586 wxChoice
* c_cat
= CastChild( ID_AUTOCATASSIGN
, wxChoice
);
589 c_cat
->Append(_("Main"));
591 for ( unsigned i
= 1; i
< theApp
->glob_prefs
->GetCatCount(); i
++ ) {
592 c_cat
->Append( theApp
->glob_prefs
->GetCategory( i
)->title
);
595 c_cat
->SetSelection( 0 );
598 void CSearchDlg::UpdateProgress(uint32 new_value
) {
599 m_progressbar
->SetValue(new_value
);
601 // File_checked_for_headers