Upstream tarball 20080414
[amule.git] / src / SearchDlg.cpp
blob0ba228361960f52294dff4b91213dce7c61793de
1 //
2 // This file is part of the aMule Project.
3 //
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 )
6 //
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
9 // respective authors.
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.
20 //
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 <wx/app.h>
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>
42 #include "Logger.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)
79 END_EVENT_TABLE()
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
98 #else
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);
104 #endif
106 // Sanity sanity
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);
112 } else {
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);
124 Layout();
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) {
140 return page;
144 return NULL;
148 void CSearchDlg::AddResult(CSearchFile* toadd)
150 CSearchListCtrl* outputwnd = GetSearchList( toadd->GetSearchID() );
152 if ( outputwnd ) {
153 outputwnd->AddResult( toadd );
155 // Update the result count
156 UpdateHitCount( outputwnd );
161 void CSearchDlg::UpdateResult(CSearchFile* toupdate)
163 CSearchListCtrl* outputwnd = GetSearchList( toupdate->GetSearchID() );
165 if ( outputwnd ) {
166 outputwnd->UpdateResult( toupdate );
168 // Update the result count
169 UpdateHitCount( outputwnd );
174 void CSearchDlg::OnListItemSelected(wxListEvent& event)
176 FindWindow(IDC_SDOWNLOAD)->Enable(true);
178 event.Skip();
182 void CSearchDlg::OnExtendedSearchChange(wxCommandEvent& event)
184 s_searchsizer->Show(s_extendedsizer, event.IsChecked());
186 Layout();
190 void CSearchDlg::OnFilterCheckChange(wxCommandEvent& event)
192 s_searchsizer->Show(s_filtersizer, event.IsChecked());
193 Layout();
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()));
214 wxASSERT(ctrl);
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(' '));
247 wxString rest;
248 if (SearchText.StartsWith(wxT("!"), &rest)) {
249 SearchText = 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() ) {
276 return;
279 wxChoice* choice = CastChild( ID_SEARCHTYPE, wxChoice );
281 // Web seaches
282 switch ( choice->GetSelection() ) {
283 // Local Search
284 case 0:
285 // Global Search
286 case 1:
287 // Kad Search
288 case 2:
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);
295 StartNewSearch();
298 break;
300 // Web Search (FileHash.com)
301 case 3:
302 theApp->amuledlg->LaunchUrl(theApp->amuledlg->GenWebSearchUrl(searchString, CamuleDlg::WS_FILEHASH));
303 break;
305 // Error
306 default:
307 wxASSERT(0);
312 void CSearchDlg::OnFieldChanged( wxEvent& WXUNUSED(evt) )
314 bool enable = false;
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 ) {
371 return true;
375 return false;
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);
394 Layout();
395 FindWindow(IDC_CLEAR_RESULTS)->Enable(true);
399 void CSearchDlg::OnBnClickedStop(wxCommandEvent& WXUNUSED(evt))
401 theApp->searchlist->StopGlobalSearch();
402 ResetControls();
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()
417 ResetControls();
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) {
427 wxString rest;
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();
438 if (sel != -1) {
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;
461 m_nSearchID++;
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()) {
474 return;
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);
493 dlg.ShowModal();
495 params.maxSize = 0;
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;
510 default:
511 AddDebugLogLineM( true, logGeneral,
512 CFormat( wxT("Warning! Unknown search-category (%s) selected!") )
513 % params.typeText
515 break;
519 SearchType search_type = KadSearch;
520 switch (CastChild( ID_SEARCHTYPE, wxChoice )->GetSelection()) {
521 case 0: // Local Search
522 search_type = LocalSearch;
523 break;
524 case 1: // Global Search
525 search_type = GlobalSearch;
526 break;
527 case 2: // Kad search
528 search_type = KadSearch;
529 break;
530 default:
531 // Should never happen
532 wxASSERT(0);
533 break;
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();
544 } else {
545 CreateNewTab(
546 ((search_type == KadSearch) ? wxT("!") : wxEmptyString) +
547 params.searchString + wxT(" (0)"),
548 real_id);
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();
563 if (hidden) {
564 searchtxt += wxString::Format(wxT(" (%u/%u)"), shown, shown + hidden);
565 } else {
566 searchtxt += wxString::Format(wxT(" (%u)"), shown);
569 m_notebook->SetPageText(i, searchtxt);
572 break;
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 );
597 c_cat->Clear();
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