Improve speed of category tab title updates
[amule.git] / src / SearchDlg.cpp
blob4951964f14631f80b3da816caca6225fd6e79746
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-2008 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.
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;
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)
78 END_EVENT_TABLE()
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 #ifdef __WXMAC__
96 //#warning TODO: restore the image list if/when wxMac supports locating the image
97 #else
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);
103 #endif
105 // Sanity sanity
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.
116 FixSearchTypes();
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);
126 Layout();
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.
142 int pos = 0;
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) {
165 return page;
169 return NULL;
173 void CSearchDlg::AddResult(CSearchFile* toadd)
175 CSearchListCtrl* outputwnd = GetSearchList( toadd->GetSearchID() );
177 if ( outputwnd ) {
178 outputwnd->AddResult( toadd );
180 // Update the result count
181 UpdateHitCount( outputwnd );
186 void CSearchDlg::UpdateResult(CSearchFile* toupdate)
188 CSearchListCtrl* outputwnd = GetSearchList( toupdate->GetSearchID() );
190 if ( outputwnd ) {
191 outputwnd->UpdateResult( toupdate );
193 // Update the result count
194 UpdateHitCount( outputwnd );
199 void CSearchDlg::OnListItemSelected(wxListEvent& event)
201 FindWindow(IDC_SDOWNLOAD)->Enable(true);
203 event.Skip();
207 void CSearchDlg::OnExtendedSearchChange(wxCommandEvent& event)
209 s_searchsizer->Show(s_extendedsizer, event.IsChecked());
211 Layout();
215 void CSearchDlg::OnFilterCheckChange(wxCommandEvent& event)
217 s_searchsizer->Show(s_filtersizer, event.IsChecked());
218 Layout();
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()));
239 wxASSERT(ctrl);
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() ) {
280 return;
283 wxChoice* choice = CastChild( ID_SEARCHTYPE, wxChoice );
285 // Magic.
287 int searchtype = choice->GetSelection();
289 if (!thePrefs::GetNetworkED2K()) {
290 searchtype += 2;
293 if (!thePrefs::GetNetworkKademlia()) {
294 searchtype += 1;
297 switch ( searchtype ) {
298 // Local Search
299 case 0:
300 // Global Search
301 case 1:
302 // Kad Search
303 case 2:
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);
310 StartNewSearch();
313 break;
315 // Web Search (FileHash.com)
316 case 3:
317 theApp->amuledlg->LaunchUrl(theApp->amuledlg->GenWebSearchUrl(searchString, CamuleDlg::WS_FILEHASH));
318 break;
320 // Error
321 default:
322 wxFAIL;
327 void CSearchDlg::OnFieldChanged( wxEvent& WXUNUSED(evt) )
329 bool enable = false;
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 ) {
386 return true;
390 return false;
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);
409 Layout();
410 FindWindow(IDC_CLEAR_RESULTS)->Enable(true);
414 void CSearchDlg::OnBnClickedStop(wxCommandEvent& WXUNUSED(evt))
416 theApp->searchlist->StopSearch();
417 ResetControls();
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()
432 ResetControls();
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)
442 wxString rest;
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();
453 if (sel != -1) {
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;
476 m_nSearchID++;
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()) {
489 return;
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);
508 dlg.ShowModal();
510 params.maxSize = 0;
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;
525 default:
526 AddDebugLogLineC( logGeneral,
527 CFormat( wxT("Warning! Unknown search-category (%s) selected!") )
528 % params.typeText
530 break;
534 SearchType search_type = KadSearch;
536 int selection = CastChild( ID_SEARCHTYPE, wxChoice )->GetSelection();
538 if (!thePrefs::GetNetworkED2K()) {
539 selection += 2;
542 if (!thePrefs::GetNetworkKademlia()) {
543 selection += 1;
546 switch (selection) {
547 case 0: // Local Search
548 search_type = LocalSearch;
549 break;
550 case 1: // Global Search
551 search_type = GlobalSearch;
552 break;
553 case 2: // Kad search
554 search_type = KadSearch;
555 break;
556 default:
557 // Should never happen
558 wxFAIL;
559 break;
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();
571 } else {
572 CreateNewTab(
573 ((search_type == KadSearch) ? wxT("!") : wxEmptyString) +
574 params.searchString + wxT(" (0)"),
575 real_id);
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();
590 if (hidden) {
591 searchtxt += CFormat(wxT(" (%u/%u)")) % shown % (shown + hidden);
592 } else {
593 searchtxt += CFormat(wxT(" (%u)")) % shown;
596 m_notebook->SetPageText(i, searchtxt);
599 break;
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 );
624 c_cat->Clear();
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