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
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
;
50 BEGIN_EVENT_TABLE(CSearchDlg
, wxPanel
)
51 EVT_BUTTON( IDC_STARTS
, CSearchDlg::OnBnClickedStart
)
52 EVT_TEXT_ENTER( IDC_SEARCHNAME
, CSearchDlg::OnBnClickedStart
)
54 EVT_BUTTON(IDC_CANCELS
, CSearchDlg::OnBnClickedStop
)
56 EVT_LIST_ITEM_SELECTED(ID_SEARCHLISTCTRL
, CSearchDlg::OnListItemSelected
)
58 EVT_BUTTON(IDC_SDOWNLOAD
, CSearchDlg::OnBnClickedDownload
)
59 EVT_BUTTON(IDC_SEARCH_RESET
, CSearchDlg::OnBnClickedReset
)
60 EVT_BUTTON(IDC_CLEAR_RESULTS
, CSearchDlg::OnBnClickedClear
)
62 EVT_CHECKBOX(IDC_EXTENDEDSEARCHCHECK
,CSearchDlg::OnExtendedSearchChange
)
63 EVT_CHECKBOX(IDC_FILTERCHECK
,CSearchDlg::OnFilterCheckChange
)
65 EVT_MULENOTEBOOK_PAGE_CLOSING(ID_NOTEBOOK
, CSearchDlg::OnSearchClosing
)
66 EVT_NOTEBOOK_PAGE_CHANGED(ID_NOTEBOOK
, CSearchDlg::OnSearchPageChanged
)
68 // Event handlers for the parameter fields getting changed
69 EVT_CUSTOM( wxEVT_COMMAND_TEXT_UPDATED
, IDC_SEARCHNAME
, CSearchDlg::OnFieldChanged
)
70 EVT_CUSTOM( wxEVT_COMMAND_TEXT_UPDATED
, IDC_EDITSEARCHEXTENSION
, CSearchDlg::OnFieldChanged
)
71 EVT_CUSTOM( wxEVT_COMMAND_SPINCTRL_UPDATED
, wxID_ANY
, CSearchDlg::OnFieldChanged
)
72 EVT_CUSTOM( wxEVT_COMMAND_CHOICE_SELECTED
, wxID_ANY
, CSearchDlg::OnFieldChanged
)
74 // Event handlers for the filter fields getting changed.
75 EVT_TEXT_ENTER(ID_FILTER_TEXT
, CSearchDlg::OnFilteringChange
)
76 EVT_CHECKBOX(ID_FILTER_INVERT
, CSearchDlg::OnFilteringChange
)
77 EVT_CHECKBOX(ID_FILTER_KNOWN
, CSearchDlg::OnFilteringChange
)
78 EVT_BUTTON(ID_FILTER
, CSearchDlg::OnFilteringChange
)
83 CSearchDlg::CSearchDlg(wxWindow
* pParent
)
84 : wxPanel(pParent
, -1)
86 m_last_search_time
= 0;
88 wxSizer
* content
= searchDlg(this, true);
89 content
->Show(this, true);
91 m_progressbar
= CastChild( ID_SEARCHPROGRESS
, wxGauge
);
92 m_progressbar
->SetRange(100);
94 m_notebook
= CastChild( ID_NOTEBOOK
, CMuleNotebook
);
96 #if defined(__WXMAC__)
97 //#warning TODO: restore the image list if/when wxMac supports locating the image
99 // Initialise the image list
100 wxImageList
* m_ImageList
= new wxImageList(16,16);
101 m_ImageList
->Add(amuleSpecial(3));
102 m_ImageList
->Add(amuleSpecial(4));
103 m_notebook
->AssignImageList(m_ImageList
);
107 wxASSERT(CastChild( ID_SEARCHTYPE
, wxChoice
)->GetString(0) == _("Local"));
108 wxASSERT(CastChild( ID_SEARCHTYPE
, wxChoice
)->GetString(2) == _("Kad"));
110 if (thePrefs::GetNetworkED2K()){
111 CastChild( ID_SEARCHTYPE
, wxChoice
)->SetSelection(0);
113 CastChild( ID_SEARCHTYPE
, wxChoice
)->SetSelection(2);
116 CastChild( IDC_TypeSearch
, wxChoice
)->SetSelection(0);
117 CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->SetSelection(2);
118 CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->SetSelection(2);
120 // Not there initially.
121 s_searchsizer
->Show(s_extendedsizer
, false);
122 s_searchsizer
->Show(s_filtersizer
, false);
128 CSearchDlg::~CSearchDlg()
133 CSearchListCtrl
* CSearchDlg::GetSearchList( wxUIntPtr id
)
135 int nPages
= m_notebook
->GetPageCount();
136 for ( int i
= 0; i
< nPages
; i
++ ) {
137 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
139 if (page
->GetSearchId() == id
) {
148 void CSearchDlg::AddResult(CSearchFile
* toadd
)
150 CSearchListCtrl
* outputwnd
= GetSearchList( toadd
->GetSearchID() );
153 outputwnd
->AddResult( toadd
);
155 // Update the result count
156 UpdateHitCount( outputwnd
);
161 void CSearchDlg::UpdateResult(CSearchFile
* toupdate
)
163 CSearchListCtrl
* outputwnd
= GetSearchList( toupdate
->GetSearchID() );
166 outputwnd
->UpdateResult( toupdate
);
168 // Update the result count
169 UpdateHitCount( outputwnd
);
174 void CSearchDlg::OnListItemSelected(wxListEvent
& event
)
176 FindWindow(IDC_SDOWNLOAD
)->Enable(true);
182 void CSearchDlg::OnExtendedSearchChange(wxCommandEvent
& event
)
184 s_searchsizer
->Show(s_extendedsizer
, event
.IsChecked());
190 void CSearchDlg::OnFilterCheckChange(wxCommandEvent
& event
)
192 s_searchsizer
->Show(s_filtersizer
, event
.IsChecked());
195 int nPages
= m_notebook
->GetPageCount();
196 for ( int i
= 0; i
< nPages
; i
++ ) {
197 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
199 page
->EnableFiltering(event
.IsChecked());
201 UpdateHitCount(page
);
206 void CSearchDlg::OnSearchClosing(wxNotebookEvent
& evt
)
208 // Abort global search if it was last tab that was closed.
209 if ( evt
.GetSelection() == ((int)m_notebook
->GetPageCount() - 1 ) ) {
210 OnBnClickedStop(nullEvent
);
213 CSearchListCtrl
*ctrl
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(evt
.GetSelection()));
215 // Zero to avoid results added while destructing.
216 ctrl
->ShowResults(0);
217 theApp
->searchlist
->RemoveResults(ctrl
->GetSearchId());
219 // Do cleanups if this was the last tab
220 if ( m_notebook
->GetPageCount() == 1 ) {
221 FindWindow(IDC_SDOWNLOAD
)->Enable(FALSE
);
222 FindWindow(IDC_CLEAR_RESULTS
)->Enable(FALSE
);
227 void CSearchDlg::OnSearchPageChanged(wxNotebookEvent
& WXUNUSED(evt
))
229 int selection
= m_notebook
->GetSelection();
231 // Workaround for a bug in wxWidgets, where deletions of pages
232 // can result in an invalid selection. This has been reported as
233 // http://sourceforge.net/tracker/index.php?func=detail&aid=1865141&group_id=9863&atid=109863
234 if (selection
>= (int)m_notebook
->GetPageCount()) {
235 selection
= m_notebook
->GetPageCount() - 1;
238 // Only enable the Download button for pages where files have been selected
239 if ( selection
!= -1 ) {
240 CSearchListCtrl
*ctrl
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(selection
));
242 bool enable
= (ctrl
->GetSelectedItemCount() > 0);
243 FindWindow(IDC_SDOWNLOAD
)->Enable( enable
);
245 // set IDC_SEARCHNAME control to search text of currently selected tab
246 wxString SearchText
= m_notebook
->GetPageText(selection
).BeforeLast(wxT(' '));
248 if (SearchText
.StartsWith(wxT("!"), &rest
)) {
252 // This check is needed because some wxWidgets (tested with
253 // 2.8.7) will return an empty string during the page-changed
254 // event resulting from the first page to being inserted. This
255 // has been reported as patch #1895161:
256 // http://sourceforge.net/tracker/index.php?func=detail&aid=1895161&group_id=9863&atid=309863
257 if (SearchText
.Length()) {
258 CastChild(IDC_SEARCHNAME
, wxTextCtrl
)->SetValue(SearchText
);
262 // Select everything.
263 CastChild(IDC_SEARCHNAME
, wxTextCtrl
)->SetSelection(-1, -1);
269 void CSearchDlg::OnBnClickedStart(wxCommandEvent
& WXUNUSED(evt
))
271 wxString searchString
= CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue();
272 searchString
.Trim(true);
273 searchString
.Trim(false);
275 if ( searchString
.IsEmpty() ) {
279 wxChoice
* choice
= CastChild( ID_SEARCHTYPE
, wxChoice
);
282 switch ( choice
->GetSelection() ) {
289 // We musn't search more often than once every 2 secs
290 if ((GetTickCount() - m_last_search_time
) > 2000) {
291 m_last_search_time
= GetTickCount();
293 OnBnClickedStop(nullEvent
);
300 // Web Search (FileHash.com)
302 theApp
->amuledlg
->LaunchUrl(theApp
->amuledlg
->GenWebSearchUrl(searchString
, CamuleDlg::WS_FILEHASH
));
312 void CSearchDlg::OnFieldChanged( wxEvent
& WXUNUSED(evt
) )
316 // These are the IDs of the search-fields
317 int textfields
[] = { IDC_SEARCHNAME
, IDC_EDITSEARCHEXTENSION
};
319 for ( uint16 i
= 0; i
< itemsof(textfields
); i
++ ) {
320 enable
|= !CastChild( textfields
[i
], wxTextCtrl
)->GetValue().IsEmpty();
323 // Check if either of the dropdowns have been changed
324 enable
|= (CastChild(IDC_SEARCHMINSIZE
, wxChoice
)->GetSelection() != 2);
325 enable
|= (CastChild(IDC_SEARCHMAXSIZE
, wxChoice
)->GetSelection() != 2);
326 enable
|= (CastChild(IDC_TypeSearch
, wxChoice
)->GetSelection() > 0);
327 enable
|= (CastChild(ID_AUTOCATASSIGN
, wxChoice
)->GetSelection() > 0);
329 // These are the IDs of the search-fields
330 int spinfields
[] = { IDC_SPINSEARCHMIN
, IDC_SPINSEARCHMAX
, IDC_SPINSEARCHAVAIBILITY
};
331 for ( uint16 i
= 0; i
< itemsof(spinfields
); i
++ ) {
332 enable
|= (CastChild( spinfields
[i
], wxSpinCtrl
)->GetValue() > 0);
335 // Enable the "Reset" button if any fields contain text
336 FindWindow(IDC_SEARCH_RESET
)->Enable( enable
);
338 // Enable the Server Search button if the Name field contains text
339 enable
= !CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue().IsEmpty();
340 FindWindow(IDC_STARTS
)->Enable( enable
);
344 void CSearchDlg::OnFilteringChange(wxCommandEvent
& WXUNUSED(evt
))
346 wxString filter
= CastChild(ID_FILTER_TEXT
, wxTextCtrl
)->GetValue();
347 bool invert
= CastChild(ID_FILTER_INVERT
, wxCheckBox
)->GetValue();
348 bool known
= CastChild(ID_FILTER_KNOWN
, wxCheckBox
)->GetValue();
350 // Check that the expression compiles before we try to assign it
351 // Otherwise we will get an error-dialog for each result-list.
352 if (wxRegEx(filter
, wxRE_DEFAULT
| wxRE_ICASE
).IsValid()) {
353 int nPages
= m_notebook
->GetPageCount();
354 for ( int i
= 0; i
< nPages
; i
++ ) {
355 CSearchListCtrl
* page
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
357 page
->SetFilter(filter
, invert
, known
);
359 UpdateHitCount(page
);
365 bool CSearchDlg::CheckTabNameExists(const wxString
& searchString
)
367 int nPages
= m_notebook
->GetPageCount();
368 for ( int i
= 0; i
< nPages
; i
++ ) {
369 // The BeforeLast(' ') is to strip the hit-count from the name
370 if ( m_notebook
->GetPageText(i
).BeforeLast(wxT(' ')) == searchString
) {
379 void CSearchDlg::CreateNewTab(const wxString
& searchString
, wxUIntPtr nSearchID
)
381 CSearchListCtrl
* list
= new CSearchListCtrl( (wxWindow
*)m_notebook
, ID_SEARCHLISTCTRL
, wxDefaultPosition
, wxDefaultSize
, wxLC_REPORT
|wxNO_BORDER
);
382 m_notebook
->AddPage(list
, searchString
, true, 0);
384 // Ensure that new results are filtered
385 bool enable
= CastChild(IDC_FILTERCHECK
, wxCheckBox
)->GetValue();
386 wxString filter
= CastChild(ID_FILTER_TEXT
, wxTextCtrl
)->GetValue();
387 bool invert
= CastChild(ID_FILTER_INVERT
, wxCheckBox
)->GetValue();
388 bool known
= CastChild(ID_FILTER_KNOWN
, wxCheckBox
)->GetValue();
390 list
->SetFilter(filter
, invert
, known
);
391 list
->EnableFiltering(enable
);
392 list
->ShowResults(nSearchID
);
395 FindWindow(IDC_CLEAR_RESULTS
)->Enable(true);
399 void CSearchDlg::OnBnClickedStop(wxCommandEvent
& WXUNUSED(evt
))
401 theApp
->searchlist
->StopGlobalSearch();
406 void CSearchDlg::ResetControls()
408 m_progressbar
->SetValue(0);
410 FindWindow(IDC_CANCELS
)->Disable();
411 FindWindow(IDC_STARTS
)->Enable(!CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue().IsEmpty());
415 void CSearchDlg::LocalSearchEnd()
420 void CSearchDlg::KadSearchEnd(uint32 id
)
422 int nPages
= m_notebook
->GetPageCount();
423 for (int i
= 0; i
< nPages
; ++i
) {
424 CSearchListCtrl
* page
=
425 dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
426 if (page
->GetSearchId() == id
) {
428 if (m_notebook
->GetPageText(i
).StartsWith(wxT("!"),&rest
)) {
429 m_notebook
->SetPageText(i
,rest
);
435 void CSearchDlg::OnBnClickedDownload(wxCommandEvent
& WXUNUSED(evt
))
437 int sel
= m_notebook
->GetSelection();
439 CSearchListCtrl
* list
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(sel
));
441 // Download with items added to category specified in the drop-down menu
442 list
->DownloadSelected();
447 void CSearchDlg::OnBnClickedClear(wxCommandEvent
& WXUNUSED(ev
))
449 OnBnClickedStop(nullEvent
);
451 m_notebook
->DeleteAllPages();
453 FindWindow(IDC_CLEAR_RESULTS
)->Enable(FALSE
);
454 FindWindow(IDC_SDOWNLOAD
)->Enable(FALSE
);
458 void CSearchDlg::StartNewSearch()
460 static uint32 m_nSearchID
= 0;
463 FindWindow(IDC_STARTS
)->Disable();
464 FindWindow(IDC_SDOWNLOAD
)->Disable();
465 FindWindow(IDC_CANCELS
)->Enable();
467 CSearchList::CSearchParams params
;
469 params
.searchString
= CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue();
470 params
.searchString
.Trim(true);
471 params
.searchString
.Trim(false);
473 if (params
.searchString
.IsEmpty()) {
477 if (CastChild(IDC_EXTENDEDSEARCHCHECK
, wxCheckBox
)->GetValue()) {
478 params
.extension
= CastChild( IDC_EDITSEARCHEXTENSION
, wxTextCtrl
)->GetValue();
480 uint32 sizemin
= GetTypeSize( (uint8
) CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->GetSelection() );
481 uint32 sizemax
= GetTypeSize( (uint8
) CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->GetSelection() );
483 // Parameter Minimum Size
484 params
.minSize
= CastChild( IDC_SPINSEARCHMIN
, wxSpinCtrl
)->GetValue() * sizemin
;
486 // Parameter Maximum Size
487 params
.maxSize
= CastChild( IDC_SPINSEARCHMAX
, wxSpinCtrl
)->GetValue() * sizemax
;
489 if ((params
.maxSize
< params
.minSize
) && (params
.maxSize
)) {
490 wxMessageDialog
dlg(this,
491 _("Min size must be smaller than max size. Max size ignored."),
492 _("Search warning"), wxOK
|wxCENTRE
|wxICON_INFORMATION
);
498 // Parameter Availability
499 params
.availability
= CastChild( IDC_SPINSEARCHAVAIBILITY
, wxSpinCtrl
)->GetValue();
501 switch ( CastChild( IDC_TypeSearch
, wxChoice
)->GetSelection() ) {
502 case 0: params
.typeText
= wxEmptyString
; break;
503 case 1: params
.typeText
= ED2KFTSTR_ARCHIVE
; break;
504 case 2: params
.typeText
= ED2KFTSTR_AUDIO
; break;
505 case 3: params
.typeText
= ED2KFTSTR_CDIMAGE
; break;
506 case 4: params
.typeText
= ED2KFTSTR_IMAGE
; break;
507 case 5: params
.typeText
= ED2KFTSTR_PROGRAM
; break;
508 case 6: params
.typeText
= ED2KFTSTR_DOCUMENT
; break;
509 case 7: params
.typeText
= ED2KFTSTR_VIDEO
; break;
511 AddDebugLogLineM( true, logGeneral
,
512 CFormat( wxT("Warning! Unknown search-category (%s) selected!") )
519 SearchType search_type
= KadSearch
;
520 switch (CastChild( ID_SEARCHTYPE
, wxChoice
)->GetSelection()) {
521 case 0: // Local Search
522 search_type
= LocalSearch
;
524 case 1: // Global Search
525 search_type
= GlobalSearch
;
527 case 2: // Kad search
528 search_type
= KadSearch
;
531 // Should never happen
535 uint32 real_id
= m_nSearchID
;
536 wxString error
= theApp
->searchlist
->StartNewSearch(&real_id
, search_type
, params
);
537 if (!error
.IsEmpty()) {
538 // Search failed / Remote in progress
539 wxMessageBox(error
, _("Search warning"),
540 wxOK
| wxCENTRE
| wxICON_INFORMATION
, this);
541 FindWindow(IDC_STARTS
)->Enable();
542 FindWindow(IDC_SDOWNLOAD
)->Disable();
543 FindWindow(IDC_CANCELS
)->Disable();
546 ((search_type
== KadSearch
) ? wxT("!") : wxEmptyString
) +
547 params
.searchString
+ wxT(" (0)"),
553 void CSearchDlg::UpdateHitCount(CSearchListCtrl
* page
)
555 for ( uint32 i
= 0; i
< (uint32
)m_notebook
->GetPageCount(); ++i
) {
556 if ( m_notebook
->GetPage(i
) == page
) {
557 wxString searchtxt
= m_notebook
->GetPageText(i
).BeforeLast(wxT(' '));
559 if ( !searchtxt
.IsEmpty() ) {
560 size_t shown
= page
->GetItemCount();
561 size_t hidden
= page
->GetHiddenItemCount();
564 searchtxt
+= wxString::Format(wxT(" (%u/%u)"), shown
, shown
+ hidden
);
566 searchtxt
+= wxString::Format(wxT(" (%u)"), shown
);
569 m_notebook
->SetPageText(i
, searchtxt
);
578 void CSearchDlg::OnBnClickedReset(wxCommandEvent
& WXUNUSED(evt
))
580 CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->Clear();
581 CastChild( IDC_EDITSEARCHEXTENSION
, wxTextCtrl
)->Clear();
582 CastChild( IDC_SPINSEARCHMIN
, wxSpinCtrl
)->SetValue(0);
583 CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->SetSelection(2);
584 CastChild( IDC_SPINSEARCHMAX
, wxSpinCtrl
)->SetValue(0);
585 CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->SetSelection(2);
586 CastChild( IDC_SPINSEARCHAVAIBILITY
, wxSpinCtrl
)->SetValue(0);
587 CastChild( IDC_TypeSearch
, wxChoice
)->SetSelection(0);
588 CastChild( ID_AUTOCATASSIGN
, wxChoice
)->SetSelection(0);
590 FindWindow(IDC_SEARCH_RESET
)->Enable(FALSE
);
594 void CSearchDlg::UpdateCatChoice()
596 wxChoice
* c_cat
= CastChild( ID_AUTOCATASSIGN
, wxChoice
);
599 c_cat
->Append(_("Main"));
601 for ( unsigned i
= 1; i
< theApp
->glob_prefs
->GetCatCount(); i
++ ) {
602 c_cat
->Append( theApp
->glob_prefs
->GetCategory( i
)->title
);
605 c_cat
->SetSelection( 0 );
608 void CSearchDlg::UpdateProgress(uint32 new_value
) {
609 m_progressbar
->SetValue(new_value
);
611 // File_checked_for_headers