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-2008 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
);
95 #if defined(__WXMAC__)
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( (wxWindow
*)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
->StopGlobalSearch();
417 theApp
->searchlist
->StopKadSearch();
422 void CSearchDlg::ResetControls()
424 m_progressbar
->SetValue(0);
426 FindWindow(IDC_CANCELS
)->Disable();
427 FindWindow(IDC_STARTS
)->Enable(!CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue().IsEmpty());
431 void CSearchDlg::LocalSearchEnd()
436 void CSearchDlg::KadSearchEnd(uint32 id
)
438 int nPages
= m_notebook
->GetPageCount();
439 for (int i
= 0; i
< nPages
; ++i
) {
440 CSearchListCtrl
* page
=
441 dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(i
));
442 if (page
->GetSearchId() == id
|| id
== 0) { // 0: just update all pages (there is only one KAD search running at a time anyway)
444 if (m_notebook
->GetPageText(i
).StartsWith(wxT("!"),&rest
)) {
445 m_notebook
->SetPageText(i
,rest
);
451 void CSearchDlg::OnBnClickedDownload(wxCommandEvent
& WXUNUSED(evt
))
453 int sel
= m_notebook
->GetSelection();
455 CSearchListCtrl
* list
= dynamic_cast<CSearchListCtrl
*>(m_notebook
->GetPage(sel
));
457 // Download with items added to category specified in the drop-down menu
458 list
->DownloadSelected();
463 void CSearchDlg::OnBnClickedClear(wxCommandEvent
& WXUNUSED(ev
))
465 OnBnClickedStop(nullEvent
);
467 m_notebook
->DeleteAllPages();
469 FindWindow(IDC_CLEAR_RESULTS
)->Enable(FALSE
);
470 FindWindow(IDC_SDOWNLOAD
)->Enable(FALSE
);
474 void CSearchDlg::StartNewSearch()
476 static uint32 m_nSearchID
= 0;
479 FindWindow(IDC_STARTS
)->Disable();
480 FindWindow(IDC_SDOWNLOAD
)->Disable();
481 FindWindow(IDC_CANCELS
)->Enable();
483 CSearchList::CSearchParams params
;
485 params
.searchString
= CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->GetValue();
486 params
.searchString
.Trim(true);
487 params
.searchString
.Trim(false);
489 if (params
.searchString
.IsEmpty()) {
493 if (CastChild(IDC_EXTENDEDSEARCHCHECK
, wxCheckBox
)->GetValue()) {
494 params
.extension
= CastChild( IDC_EDITSEARCHEXTENSION
, wxTextCtrl
)->GetValue();
496 uint32 sizemin
= GetTypeSize( (uint8
) CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->GetSelection() );
497 uint32 sizemax
= GetTypeSize( (uint8
) CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->GetSelection() );
499 // Parameter Minimum Size
500 params
.minSize
= (uint64_t)(CastChild( IDC_SPINSEARCHMIN
, wxSpinCtrl
)->GetValue()) * (uint64_t)sizemin
;
502 // Parameter Maximum Size
503 params
.maxSize
= (uint64_t)(CastChild( IDC_SPINSEARCHMAX
, wxSpinCtrl
)->GetValue()) * (uint64_t)sizemax
;
505 if ((params
.maxSize
< params
.minSize
) && (params
.maxSize
)) {
506 wxMessageDialog
dlg(this,
507 _("Min size must be smaller than max size. Max size ignored."),
508 _("Search warning"), wxOK
|wxCENTRE
|wxICON_INFORMATION
);
514 // Parameter Availability
515 params
.availability
= CastChild( IDC_SPINSEARCHAVAIBILITY
, wxSpinCtrl
)->GetValue();
517 switch ( CastChild( IDC_TypeSearch
, wxChoice
)->GetSelection() ) {
518 case 0: params
.typeText
= wxEmptyString
; break;
519 case 1: params
.typeText
= ED2KFTSTR_ARCHIVE
; break;
520 case 2: params
.typeText
= ED2KFTSTR_AUDIO
; break;
521 case 3: params
.typeText
= ED2KFTSTR_CDIMAGE
; break;
522 case 4: params
.typeText
= ED2KFTSTR_IMAGE
; break;
523 case 5: params
.typeText
= ED2KFTSTR_PROGRAM
; break;
524 case 6: params
.typeText
= ED2KFTSTR_DOCUMENT
; break;
525 case 7: params
.typeText
= ED2KFTSTR_VIDEO
; break;
527 AddDebugLogLineM( true, logGeneral
,
528 CFormat( wxT("Warning! Unknown search-category (%s) selected!") )
535 SearchType search_type
= KadSearch
;
537 int selection
= CastChild( ID_SEARCHTYPE
, wxChoice
)->GetSelection();
539 if (!thePrefs::GetNetworkED2K()) {
543 if (!thePrefs::GetNetworkKademlia()) {
548 case 0: // Local Search
549 search_type
= LocalSearch
;
551 case 1: // Global Search
552 search_type
= GlobalSearch
;
554 case 2: // Kad search
555 search_type
= KadSearch
;
558 // Should never happen
563 uint32 real_id
= m_nSearchID
;
564 wxString error
= theApp
->searchlist
->StartNewSearch(&real_id
, search_type
, params
);
565 if (!error
.IsEmpty()) {
566 // Search failed / Remote in progress
567 wxMessageBox(error
, _("Search warning"),
568 wxOK
| wxCENTRE
| wxICON_INFORMATION
, this);
569 FindWindow(IDC_STARTS
)->Enable();
570 FindWindow(IDC_SDOWNLOAD
)->Disable();
571 FindWindow(IDC_CANCELS
)->Disable();
574 ((search_type
== KadSearch
) ? wxT("!") : wxEmptyString
) +
575 params
.searchString
+ wxT(" (0)"),
581 void CSearchDlg::UpdateHitCount(CSearchListCtrl
* page
)
583 for ( uint32 i
= 0; i
< (uint32
)m_notebook
->GetPageCount(); ++i
) {
584 if ( m_notebook
->GetPage(i
) == page
) {
585 wxString searchtxt
= m_notebook
->GetPageText(i
).BeforeLast(wxT(' '));
587 if ( !searchtxt
.IsEmpty() ) {
588 size_t shown
= page
->GetItemCount();
589 size_t hidden
= page
->GetHiddenItemCount();
592 searchtxt
+= wxString::Format(wxT(" (%u/%u)"), shown
, shown
+ hidden
);
594 searchtxt
+= wxString::Format(wxT(" (%u)"), shown
);
597 m_notebook
->SetPageText(i
, searchtxt
);
606 void CSearchDlg::OnBnClickedReset(wxCommandEvent
& WXUNUSED(evt
))
608 CastChild( IDC_SEARCHNAME
, wxTextCtrl
)->Clear();
609 CastChild( IDC_EDITSEARCHEXTENSION
, wxTextCtrl
)->Clear();
610 CastChild( IDC_SPINSEARCHMIN
, wxSpinCtrl
)->SetValue(0);
611 CastChild( IDC_SEARCHMINSIZE
, wxChoice
)->SetSelection(2);
612 CastChild( IDC_SPINSEARCHMAX
, wxSpinCtrl
)->SetValue(0);
613 CastChild( IDC_SEARCHMAXSIZE
, wxChoice
)->SetSelection(2);
614 CastChild( IDC_SPINSEARCHAVAIBILITY
, wxSpinCtrl
)->SetValue(0);
615 CastChild( IDC_TypeSearch
, wxChoice
)->SetSelection(0);
616 CastChild( ID_AUTOCATASSIGN
, wxChoice
)->SetSelection(0);
618 FindWindow(IDC_SEARCH_RESET
)->Enable(FALSE
);
622 void CSearchDlg::UpdateCatChoice()
624 wxChoice
* c_cat
= CastChild( ID_AUTOCATASSIGN
, wxChoice
);
627 c_cat
->Append(_("Main"));
629 for ( unsigned i
= 1; i
< theApp
->glob_prefs
->GetCatCount(); i
++ ) {
630 c_cat
->Append( theApp
->glob_prefs
->GetCategory( i
)->title
);
633 c_cat
->SetSelection( 0 );
636 void CSearchDlg::UpdateProgress(uint32 new_value
) {
637 m_progressbar
->SetValue(new_value
);
639 // File_checked_for_headers