]>
cloud.milkyroute.net Git - dolphin.git/blob - src/search/dolphinsearchbox.cpp
1 /***************************************************************************
2 * Copyright (C) 2010 by Peter Penz <peter.penz19@gmail.com> *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 * **************************************************************************/
20 #include "dolphinsearchbox.h"
22 #include "dolphin_searchsettings.h"
23 #include "dolphinfacetswidget.h"
24 #include "panels/places/placesitemmodel.h"
26 #include <KLocalizedString>
27 #include <KNS3/KMoreToolsMenuFactory>
29 #include <config-baloo.h>
31 #include <Baloo/Query>
32 #include <Baloo/IndexerConfig>
35 #include <QButtonGroup>
37 #include <QFontDatabase>
38 #include <QHBoxLayout>
42 #include <QScrollArea>
44 #include <QToolButton>
47 DolphinSearchBox :: DolphinSearchBox ( QWidget
* parent
) :
49 m_startedSearching ( false ),
52 m_searchInput ( nullptr ),
53 m_saveSearchAction ( nullptr ),
54 m_optionsScrollArea ( nullptr ),
55 m_fileNameButton ( nullptr ),
56 m_contentButton ( nullptr ),
58 m_fromHereButton ( nullptr ),
59 m_everywhereButton ( nullptr ),
60 m_facetsToggleButton ( nullptr ),
61 m_facetsWidget ( nullptr ),
63 m_startSearchTimer ( nullptr )
67 DolphinSearchBox ::~ DolphinSearchBox ()
72 void DolphinSearchBox :: setText ( const QString
& text
)
74 m_searchInput
-> setText ( text
);
77 QString
DolphinSearchBox :: text () const
79 return m_searchInput
-> text ();
82 void DolphinSearchBox :: setSearchPath ( const QUrl
& url
)
84 if ( url
== m_searchPath
) {
88 const QUrl cleanedUrl
= url
. adjusted ( QUrl :: RemoveUserInfo
| QUrl :: StripTrailingSlash
);
90 if ( cleanedUrl
. path () == QDir :: homePath ()) {
91 m_fromHereButton
-> setChecked ( false );
92 m_everywhereButton
-> setChecked ( true );
93 if (! m_searchPath
. isEmpty ()) {
97 m_everywhereButton
-> setChecked ( false );
98 m_fromHereButton
-> setChecked ( true );
103 QFontMetrics
metrics ( m_fromHereButton
-> font ());
104 const int maxWidth
= metrics
. height () * 8 ;
106 QString location
= cleanedUrl
. fileName ();
107 if ( location
. isEmpty ()) {
108 location
= cleanedUrl
. toString ( QUrl :: PreferLocalFile
);
110 const QString elidedLocation
= metrics
. elidedText ( location
, Qt :: ElideMiddle
, maxWidth
);
111 m_fromHereButton
-> setText ( i18nc ( "action:button" , "From Here ( %1 )" , elidedLocation
));
112 m_fromHereButton
-> setToolTip ( i18nc ( "action:button" , "Limit search to ' %1 ' and its subfolders" , cleanedUrl
. toString ( QUrl :: PreferLocalFile
)));
115 QUrl
DolphinSearchBox :: searchPath () const
117 return m_everywhereButton
-> isChecked () ? QUrl :: fromLocalFile ( QDir :: homePath ()) : m_searchPath
;
120 QUrl
DolphinSearchBox :: urlForSearching () const
124 if ( isIndexingEnabled ()) {
125 url
= balooUrlForSearching ();
127 url
. setScheme ( QStringLiteral ( "filenamesearch" ));
130 query
. addQueryItem ( QStringLiteral ( "search" ), m_searchInput
-> text ());
131 if ( m_contentButton
-> isChecked ()) {
132 query
. addQueryItem ( QStringLiteral ( "checkContent" ), QStringLiteral ( "yes" ));
135 query
. addQueryItem ( QStringLiteral ( "url" ), searchPath (). url ());
143 void DolphinSearchBox :: fromSearchUrl ( const QUrl
& url
)
145 if ( url
. scheme () == QLatin1String ( "baloosearch" )) {
146 fromBalooSearchUrl ( url
);
147 } else if ( url
. scheme () == QLatin1String ( "filenamesearch" )) {
148 const QUrlQuery
query ( url
);
149 setText ( query
. queryItemValue ( QStringLiteral ( "search" )));
150 if ( m_searchPath
. scheme () != url
. scheme ()) {
151 m_searchPath
= QUrl ();
153 setSearchPath ( QUrl :: fromUserInput ( query
. queryItemValue ( QStringLiteral ( "url" )), QString (), QUrl :: AssumeLocalFile
));
154 m_contentButton
-> setChecked ( query
. queryItemValue ( QStringLiteral ( "checkContent" )) == QLatin1String ( "yes" ));
157 m_searchPath
= QUrl ();
161 setFacetsVisible ( SearchSettings :: showFacetsWidget ());
164 void DolphinSearchBox :: selectAll ()
166 m_searchInput
-> selectAll ();
169 void DolphinSearchBox :: setActive ( bool active
)
171 if ( active
!= m_active
) {
180 bool DolphinSearchBox :: isActive () const
185 bool DolphinSearchBox :: event ( QEvent
* event
)
187 if ( event
-> type () == QEvent :: Polish
) {
190 return QWidget :: event ( event
);
193 void DolphinSearchBox :: showEvent ( QShowEvent
* event
)
195 if (! event
-> spontaneous ()) {
196 m_searchInput
-> setFocus ();
197 m_startedSearching
= false ;
199 updateFacetsToggleButton ();
202 void DolphinSearchBox :: hideEvent ( QHideEvent
* event
)
205 m_startedSearching
= false ;
206 m_startSearchTimer
-> stop ();
209 void DolphinSearchBox :: keyReleaseEvent ( QKeyEvent
* event
)
211 QWidget :: keyReleaseEvent ( event
);
212 if ( event
-> key () == Qt :: Key_Escape
) {
213 if ( m_searchInput
-> text (). isEmpty ()) {
216 m_searchInput
-> clear ();
221 bool DolphinSearchBox :: eventFilter ( QObject
* obj
, QEvent
* event
)
223 switch ( event
-> type ()) {
224 case QEvent :: FocusIn
:
225 // #379135: we get the FocusIn event when we close a tab but we don't want to emit
226 // the activated() signal before the removeTab() call in DolphinTabWidget::closeTab() returns.
227 // To avoid this issue, we delay the activation of the search box.
228 // We also don't want to schedule the activation process if we are already active,
229 // otherwise we can enter in a loop of FocusIn/FocusOut events with the searchbox of another tab.
231 QTimer :: singleShot ( 0 , this , [ this ] {
242 return QObject :: eventFilter ( obj
, event
);
245 void DolphinSearchBox :: emitSearchRequest ()
247 m_startSearchTimer
-> stop ();
248 m_startedSearching
= true ;
249 m_saveSearchAction
-> setEnabled ( true );
250 emit
searchRequest ();
253 void DolphinSearchBox :: emitCloseRequest ()
255 m_startSearchTimer
-> stop ();
256 m_startedSearching
= false ;
257 m_saveSearchAction
-> setEnabled ( false );
261 void DolphinSearchBox :: slotConfigurationChanged ()
264 if ( m_startedSearching
) {
269 void DolphinSearchBox :: slotSearchTextChanged ( const QString
& text
)
272 if ( text
. isEmpty ()) {
273 m_startSearchTimer
-> stop ();
275 m_startSearchTimer
-> start ();
277 emit
searchTextChanged ( text
);
280 void DolphinSearchBox :: slotReturnPressed ()
283 emit
returnPressed ();
286 void DolphinSearchBox :: slotFacetsButtonToggled ()
288 setFacetsVisible ( m_facetsToggleButton
-> isChecked ());
291 void DolphinSearchBox :: slotFacetChanged ()
293 m_startedSearching
= true ;
294 m_startSearchTimer
-> stop ();
295 emit
searchRequest ();
298 void DolphinSearchBox :: slotSearchSaved ()
300 const QUrl searchURL
= urlForSearching ();
301 if ( searchURL
. isValid ()) {
302 PlacesItemModel model
;
303 const QString label
= i18n ( "Search for %1 in %2 " , text (), searchPath (). fileName ());
304 model
. createPlacesItem ( label
,
306 QStringLiteral ( "folder-saved-search-symbolic" ));
310 void DolphinSearchBox :: initButton ( QToolButton
* button
)
312 button
-> installEventFilter ( this );
313 button
-> setAutoExclusive ( true );
314 button
-> setAutoRaise ( true );
315 button
-> setCheckable ( true );
316 connect ( button
, & QToolButton :: clicked
, this , & DolphinSearchBox :: slotConfigurationChanged
);
319 void DolphinSearchBox :: loadSettings ()
321 if ( SearchSettings :: location () == QLatin1String ( "Everywhere" )) {
322 m_everywhereButton
-> setChecked ( true );
324 m_fromHereButton
-> setChecked ( true );
327 if ( SearchSettings :: what () == QLatin1String ( "Content" )) {
328 m_contentButton
-> setChecked ( true );
330 m_fileNameButton
-> setChecked ( true );
333 setFacetsVisible ( SearchSettings :: showFacetsWidget ());
336 void DolphinSearchBox :: saveSettings ()
338 SearchSettings :: setLocation ( m_fromHereButton
-> isChecked () ? QStringLiteral ( "FromHere" ) : QStringLiteral ( "Everywhere" ));
339 SearchSettings :: setWhat ( m_fileNameButton
-> isChecked () ? QStringLiteral ( "FileName" ) : QStringLiteral ( "Content" ));
340 if ( isIndexingEnabled ()) {
341 SearchSettings :: setShowFacetsWidget ( m_facetsToggleButton
-> isChecked ());
343 SearchSettings :: self ()-> save ();
346 void DolphinSearchBox :: init ()
348 // Create close button
349 QToolButton
* closeButton
= new QToolButton ( this );
350 closeButton
-> setAutoRaise ( true );
351 closeButton
-> setIcon ( QIcon :: fromTheme ( QStringLiteral ( "dialog-close" )));
352 closeButton
-> setToolTip ( i18nc ( "@info:tooltip" , "Quit searching" ));
353 connect ( closeButton
, & QToolButton :: clicked
, this , & DolphinSearchBox :: emitCloseRequest
);
356 m_searchInput
= new QLineEdit ( this );
357 m_searchInput
-> setPlaceholderText ( i18n ( "Search..." ));
358 m_searchInput
-> installEventFilter ( this );
359 m_searchInput
-> setClearButtonEnabled ( true );
360 m_searchInput
-> setFont ( QFontDatabase :: systemFont ( QFontDatabase :: GeneralFont
));
361 connect ( m_searchInput
, & QLineEdit :: returnPressed
,
362 this , & DolphinSearchBox :: slotReturnPressed
);
363 connect ( m_searchInput
, & QLineEdit :: textChanged
,
364 this , & DolphinSearchBox :: slotSearchTextChanged
);
365 setFocusProxy ( m_searchInput
);
367 // Add "Save search" button inside search box
368 m_saveSearchAction
= new QAction ( this );
369 m_saveSearchAction
-> setIcon ( QIcon :: fromTheme ( QStringLiteral ( "document-save-symbolic" )));
370 m_saveSearchAction
-> setText ( i18nc ( "action:button" , "Save this search to quickly access it again in the future" ));
371 m_saveSearchAction
-> setEnabled ( false );
372 m_searchInput
-> addAction ( m_saveSearchAction
, QLineEdit :: TrailingPosition
);
373 connect ( m_saveSearchAction
, & QAction :: triggered
, this , & DolphinSearchBox :: slotSearchSaved
);
375 // Apply layout for the search input
376 QHBoxLayout
* searchInputLayout
= new QHBoxLayout ();
377 searchInputLayout
-> setContentsMargins ( 0 , 0 , 0 , 0 );
378 searchInputLayout
-> addWidget ( closeButton
);
379 searchInputLayout
-> addWidget ( m_searchInput
);
381 // Create "Filename" and "Content" button
382 m_fileNameButton
= new QToolButton ( this );
383 m_fileNameButton
-> setText ( i18nc ( "action:button" , "Filename" ));
384 initButton ( m_fileNameButton
);
386 m_contentButton
= new QToolButton ();
387 m_contentButton
-> setText ( i18nc ( "action:button" , "Content" ));
388 initButton ( m_contentButton
);
390 QButtonGroup
* searchWhatGroup
= new QButtonGroup ( this );
391 searchWhatGroup
-> addButton ( m_fileNameButton
);
392 searchWhatGroup
-> addButton ( m_contentButton
);
394 m_separator
= new KSeparator ( Qt :: Vertical
, this );
396 // Create "From Here" and "Your files" buttons
397 m_fromHereButton
= new QToolButton ( this );
398 m_fromHereButton
-> setText ( i18nc ( "action:button" , "From Here" ));
399 initButton ( m_fromHereButton
);
401 m_everywhereButton
= new QToolButton ( this );
402 m_everywhereButton
-> setText ( i18nc ( "action:button" , "Your files" ));
403 m_everywhereButton
-> setToolTip ( i18nc ( "action:button" , "Search in your home directory" ));
404 m_everywhereButton
-> setIcon ( QIcon :: fromTheme ( QStringLiteral ( "user-home" )));
405 m_everywhereButton
-> setToolButtonStyle ( Qt :: ToolButtonTextBesideIcon
);
406 initButton ( m_everywhereButton
);
408 QButtonGroup
* searchLocationGroup
= new QButtonGroup ( this );
409 searchLocationGroup
-> addButton ( m_fromHereButton
);
410 searchLocationGroup
-> addButton ( m_everywhereButton
);
412 auto moreSearchToolsButton
= new QToolButton ( this );
413 moreSearchToolsButton
-> setAutoRaise ( true );
414 moreSearchToolsButton
-> setPopupMode ( QToolButton :: InstantPopup
);
415 moreSearchToolsButton
-> setIcon ( QIcon :: fromTheme ( "arrow-down-double" ));
416 moreSearchToolsButton
-> setToolButtonStyle ( Qt :: ToolButtonTextBesideIcon
);
417 moreSearchToolsButton
-> setText ( i18n ( "More Search Tools" ));
418 moreSearchToolsButton
-> setMenu ( new QMenu ( this ));
419 connect ( moreSearchToolsButton
-> menu (), & QMenu :: aboutToShow
, moreSearchToolsButton
-> menu (), [ this , moreSearchToolsButton
]()
421 m_menuFactory
. reset ( new KMoreToolsMenuFactory ( "dolphin/search-tools" ));
422 moreSearchToolsButton
-> menu ()-> clear ();
423 m_menuFactory
-> fillMenuFromGroupingNames ( moreSearchToolsButton
-> menu (), { "files-find" }, this -> m_searchPath
);
426 // Create "Facets" widgets
427 m_facetsToggleButton
= new QToolButton ( this );
428 m_facetsToggleButton
-> setToolButtonStyle ( Qt :: ToolButtonTextBesideIcon
);
429 initButton ( m_facetsToggleButton
);
430 connect ( m_facetsToggleButton
, & QToolButton :: clicked
, this , & DolphinSearchBox :: slotFacetsButtonToggled
);
432 m_facetsWidget
= new DolphinFacetsWidget ( this );
433 m_facetsWidget
-> installEventFilter ( this );
434 m_facetsWidget
-> setSizePolicy ( QSizePolicy :: Preferred
, QSizePolicy :: Maximum
);
435 connect ( m_facetsWidget
, & DolphinFacetsWidget :: facetChanged
, this , & DolphinSearchBox :: slotFacetChanged
);
437 // Apply layout for the options
438 QHBoxLayout
* optionsLayout
= new QHBoxLayout ();
439 optionsLayout
-> setContentsMargins ( 0 , 0 , 0 , 0 );
440 optionsLayout
-> addWidget ( m_fileNameButton
);
441 optionsLayout
-> addWidget ( m_contentButton
);
442 optionsLayout
-> addWidget ( m_separator
);
443 optionsLayout
-> addWidget ( m_fromHereButton
);
444 optionsLayout
-> addWidget ( m_everywhereButton
);
445 optionsLayout
-> addWidget ( new KSeparator ( Qt :: Vertical
, this ));
446 optionsLayout
-> addWidget ( m_facetsToggleButton
);
447 optionsLayout
-> addWidget ( moreSearchToolsButton
);
448 optionsLayout
-> addStretch ( 1 );
450 // Put the options into a QScrollArea. This prevents increasing the view width
451 // in case that not enough width for the options is available.
452 QWidget
* optionsContainer
= new QWidget ( this );
453 optionsContainer
-> setLayout ( optionsLayout
);
455 m_optionsScrollArea
= new QScrollArea ( this );
456 m_optionsScrollArea
-> setFrameShape ( QFrame :: NoFrame
);
457 m_optionsScrollArea
-> setHorizontalScrollBarPolicy ( Qt :: ScrollBarAlwaysOff
);
458 m_optionsScrollArea
-> setVerticalScrollBarPolicy ( Qt :: ScrollBarAlwaysOff
);
459 m_optionsScrollArea
-> setMaximumHeight ( optionsContainer
-> sizeHint (). height ());
460 m_optionsScrollArea
-> setSizePolicy ( QSizePolicy :: Preferred
, QSizePolicy :: Fixed
);
461 m_optionsScrollArea
-> setWidget ( optionsContainer
);
462 m_optionsScrollArea
-> setWidgetResizable ( true );
464 m_topLayout
= new QVBoxLayout ( this );
465 m_topLayout
-> setContentsMargins ( 0 , 0 , 0 , 0 );
466 m_topLayout
-> addLayout ( searchInputLayout
);
467 m_topLayout
-> addWidget ( m_optionsScrollArea
);
468 m_topLayout
-> addWidget ( m_facetsWidget
);
472 // The searching should be started automatically after the user did not change
473 // the text within one second
474 m_startSearchTimer
= new QTimer ( this );
475 m_startSearchTimer
-> setSingleShot ( true );
476 m_startSearchTimer
-> setInterval ( 1000 );
477 connect ( m_startSearchTimer
, & QTimer :: timeout
, this , & DolphinSearchBox :: emitSearchRequest
);
480 QUrl
DolphinSearchBox :: balooUrlForSearching () const
483 const QString text
= m_searchInput
-> text ();
486 query
. addType ( m_facetsWidget
-> facetType ());
488 QStringList queryStrings
;
489 QString ratingQuery
= m_facetsWidget
-> ratingTerm ();
490 if (! ratingQuery
. isEmpty ()) {
491 queryStrings
<< ratingQuery
;
494 if ( m_contentButton
-> isChecked ()) {
495 queryStrings
<< text
;
496 } else if (! text
. isEmpty ()) {
497 queryStrings
<< QStringLiteral ( "filename: \" %1 \" " ). arg ( text
);
500 if ( m_fromHereButton
-> isChecked ()) {
501 query
. setIncludeFolder ( m_searchPath
. toLocalFile ());
504 query
. setSearchString ( queryStrings
. join ( QLatin1Char ( ' ' )));
506 return query
. toSearchUrl ( i18nc ( "@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered." ,
507 "Query Results from ' %1 '" , text
));
513 void DolphinSearchBox :: fromBalooSearchUrl ( const QUrl
& url
)
516 const Baloo :: Query query
= Baloo :: Query :: fromSearchUrl ( url
);
518 // Block all signals to avoid unnecessary "searchRequest" signals
519 // while we adjust the search text and the facet widget.
522 const QString customDir
= query
. includeFolder ();
523 if (! customDir
. isEmpty ()) {
524 setSearchPath ( QUrl :: fromLocalFile ( customDir
));
526 setSearchPath ( QUrl :: fromLocalFile ( QDir :: homePath ()));
529 m_facetsWidget
-> resetOptions ();
531 setText ( query
. searchString ());
533 QStringList types
= query
. types ();
534 if (! types
. isEmpty ()) {
535 m_facetsWidget
-> setFacetType ( types
. first ());
538 const QStringList subTerms
= query
. searchString (). split ( ' ' , QString :: SkipEmptyParts
);
539 foreach ( const QString
& subTerm
, subTerms
) {
540 if ( subTerm
. startsWith ( QLatin1String ( "filename:" ))) {
541 const QString value
= subTerm
. mid ( 9 );
543 } else if ( m_facetsWidget
-> isRatingTerm ( subTerm
)) {
544 m_facetsWidget
-> setRatingTerm ( subTerm
);
548 m_startSearchTimer
-> stop ();
555 void DolphinSearchBox :: setFacetsVisible ( bool visible
)
557 const bool indexingEnabled
= isIndexingEnabled ();
558 m_facetsWidget
-> setEnabled ( indexingEnabled
);
559 m_facetsWidget
-> setVisible ( indexingEnabled
&& visible
);
560 updateFacetsToggleButton ();
563 void DolphinSearchBox :: updateFacetsToggleButton ()
565 const bool facetsEnabled
= m_facetsWidget
-> isEnabled ();
566 const bool facetsVisible
= m_facetsWidget
-> isVisible ();
568 m_facetsToggleButton
-> setEnabled ( facetsEnabled
);
569 m_facetsToggleButton
-> setChecked ( facetsVisible
);
571 m_facetsToggleButton
-> setIcon ( QIcon :: fromTheme (
572 facetsVisible
? QStringLiteral ( "arrow-up-double" ) :
573 QStringLiteral ( "arrow-down-double" )));
575 m_facetsToggleButton
-> setText (
576 facetsVisible
? i18nc ( "@action:button" , "Fewer Options" ) :
577 i18nc ( "@action:button" , "More Options" ));
580 m_facetsToggleButton
-> setToolTip ( QString ());
583 const Baloo :: IndexerConfig searchInfo
;
584 if (! searchInfo
. fileIndexingEnabled ()) {
585 m_facetsToggleButton
-> setToolTip ( i18nc ( "@info:tooltip" , "Advanced search options are not available because the file indexing service is disabled." ));
587 m_facetsToggleButton
-> setToolTip ( i18nc ( "@info:tooltip" , "Advanced search options are not available because this location is not indexed." ));
590 m_facetsToggleButton
-> setToolTip ( i18nc ( "@info:tooltip" , "Advanced search options are not available because this version of Dolphin does not support the Baloo file indexer." ));
595 bool DolphinSearchBox :: isIndexingEnabled () const
598 const Baloo :: IndexerConfig searchInfo
;
599 return searchInfo
. fileIndexingEnabled () && searchInfo
. shouldBeIndexed ( searchPath (). toLocalFile ());