1 /*
2     SPDX-FileCopyrightText: 1999 Matthias Elter <me@kde.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "navigator.h"
8 
9 #include <QApplication>
10 #include <QDir>
11 #include <QProgressBar>
12 #include <QStandardPaths>
13 #include <QTabWidget>
14 #include <QTreeWidget>
15 #include <QTreeWidgetItemIterator>
16 #include <QVBoxLayout>
17 
18 #include <KDesktopFile>
19 #include <KLineEdit>
20 #include <KMessageBox>
21 #include <KProcess>
22 #include <KProtocolInfo>
23 #include <KServiceGroup>
24 #include <KServiceTypeTrader>
25 #include <KShell>
26 
27 #include "navigatoritem.h"
28 #include "navigatorappitem.h"
29 #include "navigatorappgroupitem.h"
30 #include "searchwidget.h"
31 #include "searchengine.h"
32 #include "searchhandler.h"
33 #include "docmetainfo.h"
34 #include "docentrytraverser.h"
35 #include "toc.h"
36 #include "view.h"
37 #include "infotree.h"
38 #include "plugintraverser.h"
39 #include "scrollkeepertreebuilder.h"
40 #include "history.h"
41 #include "khc_debug.h"
42 #include "grantleeformatter.h"
43 
44 #include <prefs.h>
45 
46 using namespace KHC;
47 
Navigator(View * view,QWidget * parent)48 Navigator::Navigator( View *view, QWidget *parent )
49    : QWidget( parent ),
50      mView( view ), mSelected( false ), mIndexingProc( nullptr )
51 {
52     mSearchEngine = new SearchEngine( view );
53     connect(mSearchEngine, &SearchEngine::searchFinished, this, &Navigator::slotSearchFinished);
54 
55     DocMetaInfo::self()->scanMetaInfo();
56 
57     QBoxLayout *topLayout = new QVBoxLayout( this );
58 
59     mSearchEdit = new KLineEdit(this);
60     mSearchEdit->setPlaceholderText( i18n("Search...") );
61     mSearchEdit->setClearButtonEnabled(true);
62     topLayout->addWidget( mSearchEdit );
63     connect(mSearchEdit, &KLineEdit::returnKeyPressed, this, &Navigator::slotSearch);
64     connect(mSearchEdit, &KLineEdit::textChanged, this, &Navigator::checkSearchEdit);
65 
66     mTabWidget = new QTabWidget( this );
67     topLayout->addWidget( mTabWidget );
68 
69     mIndexingBar = new QProgressBar( this );
70     mIndexingBar->hide();
71     topLayout->addWidget( mIndexingBar );
72 
73     mIndexingTimer.setSingleShot( true );
74     mIndexingTimer.setInterval( 1000 );
75     connect( &mIndexingTimer, &QTimer::timeout, this, &Navigator::slotShowIndexingProgressBar );
76 
77     setupContentsTab();
78     setupGlossaryTab();
79     setupSearchTab();
80 
81     insertPlugins();
82 
83     if ( !mSearchEngine->initSearchHandlers() ) {
84       hideSearch();
85     } else {
86       mSearchWidget->updateScopeList();
87       mSearchWidget->readConfig( KSharedConfig::openConfig().data() );
88       QTimer::singleShot( 0, this, &Navigator::slotDelayedIndexingStart );
89     }
90 }
91 
~Navigator()92 Navigator::~Navigator()
93 {
94   delete mSearchEngine;
95 }
96 
searchEngine() const97 SearchEngine *Navigator::searchEngine() const
98 {
99   return mSearchEngine;
100 }
101 
setupContentsTab()102 void Navigator::setupContentsTab()
103 {
104     mContentsTree = new QTreeWidget( mTabWidget );
105     mContentsTree->setFrameStyle( QFrame::NoFrame );
106     mContentsTree->setAllColumnsShowFocus(true);
107     mContentsTree->setRootIsDecorated(false);
108     mContentsTree->headerItem()->setHidden(true);
109     mContentsTree->setExpandsOnDoubleClick(false);
110 
111     connect(mContentsTree, &QTreeWidget::itemActivated, this, &Navigator::slotItemSelected);
112     connect(mContentsTree, &QTreeWidget::itemExpanded, this, &Navigator::slotItemExpanded);
113     connect(mContentsTree, &QTreeWidget::itemCollapsed, this, &Navigator::slotItemCollapsed);
114 
115     mTabWidget->addTab(mContentsTree, i18n("&Contents"));
116 }
117 
setupSearchTab()118 void Navigator::setupSearchTab()
119 {
120 
121     mSearchWidget = new SearchWidget( mSearchEngine, mTabWidget );
122     connect(mSearchWidget, &SearchWidget::searchResult, this, &Navigator::slotShowSearchResult);
123     connect(mSearchWidget, &SearchWidget::scopeCountChanged, this, &Navigator::checkSearchEdit);
124 
125     mTabWidget->addTab( mSearchWidget, i18n("Search Options"));
126 
127 }
128 
setupGlossaryTab()129 void Navigator::setupGlossaryTab()
130 {
131     mGlossaryTree = new Glossary( mTabWidget );
132     connect(mGlossaryTree, &Glossary::entrySelected, this, &Navigator::glossSelected);
133     mTabWidget->addTab( mGlossaryTree, i18n( "G&lossary" ) );
134 }
135 
insertPlugins()136 void Navigator::insertPlugins()
137 {
138   PluginTraverser t( this, mContentsTree );
139   DocMetaInfo::self()->traverseEntries( &t );
140 }
141 
insertParentAppDocs(const QString & name,NavigatorItem * topItem)142 void Navigator::insertParentAppDocs( const QString &name, NavigatorItem *topItem )
143 {
144   qCDebug(KHC_LOG) << "Requested plugin documents for ID " << name;
145 
146   KServiceGroup::Ptr grp = KServiceGroup::childGroup( name );
147   if ( !grp )
148     return;
149 
150   KServiceGroup::List entries = grp->entries();
151   KServiceGroup::List::ConstIterator it = entries.constBegin();
152   KServiceGroup::List::ConstIterator end = entries.constEnd();
153   for ( ; it != end; ++it ) {
154     QString desktopFile = ( *it )->entryPath();
155     if ( QDir::isRelativePath( desktopFile ) )
156         desktopFile = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, desktopFile );
157     createItemFromDesktopFile( topItem, desktopFile );
158   }
159 }
160 
insertKCMDocs(const QString & name,NavigatorItem * topItem,const QString & type)161 void Navigator::insertKCMDocs( const QString &name, NavigatorItem *topItem, const QString &type )
162 {
163   qCDebug(KHC_LOG) << "Requested KCM documents for ID" << name;
164   QString systemsettingskontrolconstraint = QStringLiteral("[X-KDE-System-Settings-Parent-Category] != ''");
165   QString konquerorcontrolconstraint = QStringLiteral("[X-KDE-PluginKeyword] == 'khtml_general'\
166                                      or [X-KDE-PluginKeyword] == 'performance'\
167                                      or [X-KDE-PluginKeyword] == 'bookmarks'");
168   QString filemanagercontrolconstraint = QStringLiteral("[X-KDE-PluginKeyword] == 'behavior'\
169                                        or [X-KDE-PluginKeyword] == 'dolphinviewmodes'\
170                                        or [X-KDE-PluginKeyword] == 'dolphinnavigation'\
171                                        or [X-KDE-PluginKeyword] == 'dolphinservices'\
172                                        or [X-KDE-PluginKeyword] == 'dolphingeneral'\
173                                        or [X-KDE-PluginKeyword] == 'trash'");
174   QString browsercontrolconstraint = QStringLiteral("[X-KDE-PluginKeyword] == 'khtml_behavior'\
175                                    or [X-KDE-PluginKeyword] == 'proxy'\
176                                    or [X-KDE-PluginKeyword] == 'khtml_appearance'\
177                                    or [X-KDE-PluginKeyword] == 'khtml_filter'\
178                                    or [X-KDE-PluginKeyword] == 'cache'\
179                                    or [X-KDE-PluginKeyword] == 'cookie'\
180                                    or [X-KDE-PluginKeyword] == 'useragent'\
181                                    or [X-KDE-PluginKeyword] == 'khtml_java_js'\
182                                    or [X-KDE-PluginKeyword] == 'khtml_plugins'");
183 /* missing in browsercontrolconstraint
184 History                 no X-KDE-PluginKeyword in kcmhistory.desktop
185 */
186   QString othercontrolconstraint = QStringLiteral("[X-KDE-PluginKeyword] == 'cgi'");
187 
188   KService::List list;
189 
190   if ( type == QLatin1String("kcontrol") ) {
191     list = KServiceTypeTrader::self()->query( QStringLiteral("KCModule"), systemsettingskontrolconstraint );
192   } else if ( type == QLatin1String("konquerorcontrol") ) {
193     list = KServiceTypeTrader::self()->query( QStringLiteral("KCModule"), konquerorcontrolconstraint );
194   } else if ( type == QLatin1String("browsercontrol") ) {
195     list = KServiceTypeTrader::self()->query( QStringLiteral("KCModule"), browsercontrolconstraint );
196   } else if ( type == QLatin1String("filemanagercontrol") ) {
197     list = KServiceTypeTrader::self()->query( QStringLiteral("KCModule"), filemanagercontrolconstraint );
198   } else if ( type == QLatin1String("othercontrol") ) {
199     list = KServiceTypeTrader::self()->query( QStringLiteral("KCModule"), othercontrolconstraint );
200   } else if ( type == QLatin1String("kinfocenter") ) {
201     list = KServiceTypeTrader::self()->query( QStringLiteral("KCModule"), QStringLiteral("[X-KDE-ParentApp] == 'kinfocenter'") );
202   }
203 
204   bool no_children_present = true;
205 
206   for ( KService::List::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
207   {
208     KService::Ptr s(*it);
209     const QString desktopFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/") + s->entryPath() );
210     createItemFromDesktopFile( topItem, desktopFile );
211     no_children_present = false;
212     }
213     topItem->sortChildren( 0, Qt::AscendingOrder /* ascending */ );
214     topItem->setHidden(no_children_present);
215 }
216 
insertIOSlaveDocs(const QString & name,NavigatorItem * topItem)217 void Navigator::insertIOSlaveDocs( const QString &name, NavigatorItem *topItem )
218 {
219   qCDebug(KHC_LOG) << "Requested IOSlave documents for ID" << name;
220 
221   QStringList list = KProtocolInfo::protocols();
222   list.sort();
223 
224   NavigatorItem *prevItem = nullptr;
225   for ( QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it )
226   {
227     QString docPath = KProtocolInfo::docPath(*it);
228     if ( !docPath.isNull() )
229     {
230       // First parameter is ignored if second is an absolute path
231       QUrl url(QStringLiteral("help:/") + docPath);
232       QString icon = KProtocolInfo::icon(*it);
233       if ( icon.isEmpty() ) icon = QStringLiteral("text-plain");
234       DocEntry *entry = new DocEntry( *it, url.url(), icon );
235       NavigatorItem *item = new NavigatorAppItem( entry, topItem, prevItem );
236       prevItem = item;
237       item->setAutoDeleteDocEntry( true );
238     }
239   }
240 }
241 
createItemFromDesktopFile(NavigatorItem * topItem,const QString & file)242 void Navigator::createItemFromDesktopFile( NavigatorItem *topItem,
243                                            const QString &file )
244 {
245     KDesktopFile desktopFile( file );
246     QString docPath = desktopFile.readDocPath();
247     if ( !docPath.isNull() ) {
248       // First parameter is ignored if second is an absolute path
249       const QUrl url(QStringLiteral("help:/") + docPath);
250       QString icon = desktopFile.readIcon();
251       if ( icon.isEmpty() ) icon = QStringLiteral("text-plain");
252       DocEntry *entry = new DocEntry( desktopFile.readName(), url.url(), icon );
253       NavigatorItem *item = new NavigatorAppItem( entry, topItem );
254       item->setAutoDeleteDocEntry( true );
255     }
256 }
257 
insertInfoDocs(NavigatorItem * topItem)258 void Navigator::insertInfoDocs( NavigatorItem *topItem )
259 {
260   InfoTree *infoTree = new InfoTree( this );
261   infoTree->build( topItem );
262 }
263 
insertScrollKeeperDocs(NavigatorItem * topItem)264 void Navigator::insertScrollKeeperDocs( NavigatorItem *topItem )
265 {
266   ScrollKeeperTreeBuilder *builder = new ScrollKeeperTreeBuilder( this );
267   builder->buildOrHide( topItem );
268 }
269 
selectItem(const QUrl & url)270 void Navigator::selectItem( const QUrl &url )
271 {
272   qCDebug(KHC_LOG) << "Navigator::selectItem(): " << url.url();
273 
274   if ( url.url() == QLatin1String("khelpcenter:home") ) {
275     clearSelection();
276     return;
277   }
278 
279   // help:/foo&anchor=bar gets redirected to help:/foo#bar
280   // Make sure that we match both the original URL as well as
281   // its counterpart.
282   QUrl alternativeURL = url;
283   QUrl contentsItemURL = url;
284   if (url.hasFragment())
285   {
286      alternativeURL.setQuery(QStringLiteral("anchor=")+url.fragment());
287      alternativeURL.setFragment(QString());
288      contentsItemURL.setFragment(QString());
289   }
290 
291   // If the navigator already has the given URL selected, do nothing.
292   NavigatorItem *item;
293   item = static_cast<NavigatorItem *>( mContentsTree->currentItem() );
294   if ( item && mSelected ) {
295     const QUrl currentURL ( item->entry()->url() );
296     if ( (currentURL == url) || (currentURL == alternativeURL) ) {
297       qCDebug(KHC_LOG) << "URL already shown.";
298       return;
299     }
300   }
301 
302   // First, populate the NavigatorAppGroupItems if we don't want the home page
303   if ( url != homeURL() ) {
304     QTreeWidgetItemIterator it1( mContentsTree );
305     while( (*it1) )
306     {
307       NavigatorAppGroupItem *appItem = dynamic_cast<NavigatorAppGroupItem *>( (*it1) );
308       if ( appItem ) appItem->populate( true );
309       ++it1;
310     }
311   }
312 
313   NavigatorItem *contentsItem = nullptr;
314   QTreeWidgetItemIterator it( mContentsTree );
315   while ( (*it) ) {
316     NavigatorItem *item = static_cast<NavigatorItem *>( (*it) );
317     const QUrl itemUrl( item->entry()->url() );
318     if ( (itemUrl == url) || (itemUrl == alternativeURL) ) {
319       // If the current item was not selected and remained unchanged it
320       // needs to be explicitly selected
321       mContentsTree->setCurrentItem(item);
322       item->setExpanded( true );
323       break;
324     }
325     if ( (contentsItem == nullptr) && (itemUrl == contentsItemURL) ) {
326       contentsItem = item;
327     }
328     ++it;
329   }
330   if ( !(*it) ) {
331     // if search with fragment didn't find anything, but item without fragment was found, use it
332     if ( contentsItem != nullptr ) {
333       mContentsTree->setCurrentItem(contentsItem);
334       contentsItem->setExpanded( true );
335       mSelected = true;
336     } else {
337       clearSelection();
338     }
339   } else {
340     mSelected = true;
341   }
342 }
343 
clearSelection()344 void Navigator::clearSelection()
345 {
346   mContentsTree->clearSelection();
347   mSelected = false;
348 }
349 
slotItemSelected(QTreeWidgetItem * currentItem)350 void Navigator::slotItemSelected( QTreeWidgetItem *currentItem )
351 {
352   if ( !currentItem ) return;
353 
354   mSelected = true;
355 
356   NavigatorItem *item = static_cast<NavigatorItem *>( currentItem );
357 
358   qCDebug(KHC_LOG) << item->entry()->name();
359 
360   item->setExpanded( !item->isExpanded() );
361 
362   const QUrl url ( item->entry()->url() );
363 
364   if ( url.scheme() == QLatin1String("khelpcenter") ) {
365       mView->closeUrl();
366       History::self().updateCurrentEntry( mView );
367       History::self().createEntry();
368       showOverview( item, url );
369   } else {
370 
371     Q_EMIT itemSelected( url.url() );
372   }
373 }
374 
slotItemExpanded(QTreeWidgetItem * item)375 void Navigator::slotItemExpanded( QTreeWidgetItem *item )
376 {
377   NavigatorItem *ni = static_cast<NavigatorItem *>( item );
378   ni->itemExpanded( true );
379 }
380 
slotItemCollapsed(QTreeWidgetItem * item)381 void Navigator::slotItemCollapsed( QTreeWidgetItem *item )
382 {
383   NavigatorItem *ni = static_cast<NavigatorItem *>( item );
384   ni->itemExpanded( false );
385 }
386 
openInternalUrl(const QUrl & url)387 void Navigator::openInternalUrl( const QUrl &url )
388 {
389   if ( url.url() == QLatin1String("khelpcenter:home") ) {
390     clearSelection();
391     showOverview( nullptr, url );
392     return;
393   }
394 
395   selectItem( url );
396   if ( !mSelected ) return;
397 
398   NavigatorItem *item =
399     static_cast<NavigatorItem *>( mContentsTree->currentItem() );
400 
401   if ( item ) showOverview( item, url );
402 }
403 
showOverview(NavigatorItem * item,const QUrl & url)404 void Navigator::showOverview( NavigatorItem *item, const QUrl &url )
405 {
406   mView->beginInternal( url );
407 
408   QString title,name,content;
409   uint childCount;
410 
411   if ( item ) {
412     title = item->entry()->name();
413     name = item->entry()->name();
414 
415     const QString info = item->entry()->info();
416     if ( !info.isEmpty() ) content = QLatin1String("<p>") + info + QLatin1String("</p>\n");
417 
418     childCount = item->childCount();
419   } else {
420     title = i18n("Start Page");
421     name = i18n("KDE Help Center");
422 
423     childCount = mContentsTree->topLevelItemCount();
424   }
425 
426   if ( childCount > 0 ) {
427     QTreeWidgetItem *child;
428     if ( item ) child = item;
429     else child = mContentsTree->invisibleRootItem();
430 
431     content += createChildrenList( child, 0 );
432   }
433   else
434     content += QLatin1String("<p></p>");
435 
436   mView->write( mView->grantleeFormatter()->formatOverview( title, name, content ) );
437 
438   mView->end();
439 }
440 
createChildrenList(QTreeWidgetItem * child,int level)441 QString Navigator::createChildrenList( QTreeWidgetItem *child, int level )
442 {
443   QString t;
444 
445   t += QLatin1String("<ul>\n");
446 
447   int cc = child->childCount();
448   for (int i=0;i<cc;i++)
449   {
450     NavigatorItem *childItem = static_cast<NavigatorItem *>( child->child(i) );
451 
452     DocEntry *e = childItem->entry();
453 
454     t += QLatin1String("<li><a href=\"") + e->url() + QLatin1String("\">");
455     if ( e->isDirectory() ) t += QLatin1String("<b>");
456     t += e->name();
457     if ( e->isDirectory() ) t += QLatin1String("</b>");
458     t += QLatin1String("</a>");
459 
460     if ( !e->info().isEmpty() ) {
461       t += QLatin1String("<br>") + e->info();
462     }
463 
464     if ( childItem->childCount() > 0 && level < 1 ) {
465       t += createChildrenList( childItem, level + 1 );
466     }
467 
468     t += QLatin1String("</li>\n");
469 
470   }
471 
472   t += QLatin1String("</ul>\n");
473 
474   return t;
475 }
476 
slotSearch()477 void Navigator::slotSearch()
478 {
479 
480   qCDebug(KHC_LOG) << "Navigator::slotSearch()";
481 
482   if ( mIndexingProc ) return;
483 
484   if ( mSearchEngine->isRunning() ) return;
485 
486   const QString words = mSearchEdit->text();
487   const QString method = mSearchWidget->method();
488   const int pages = mSearchWidget->pages();
489   const QStringList scope = mSearchWidget->scope();
490 
491   qCDebug(KHC_LOG) << "Navigator::slotSearch() words: " << words;
492   qCDebug(KHC_LOG) << "Navigator::slotSearch() scope: " << scope;
493 
494   if ( words.isEmpty() || scope.isEmpty() ) return;
495 
496   mTabWidget->setCurrentIndex( mTabWidget->indexOf( mSearchWidget ) );
497 
498   // disable search edit during searches
499   mSearchEdit->setEnabled(false);
500   QApplication::setOverrideCursor(Qt::WaitCursor);
501 
502   if ( !mSearchEngine->search( words, method, pages, scope ) ) {
503     slotSearchFinished();
504     KMessageBox::sorry( this, i18n("Unable to run search program.") );
505   }
506 
507 }
508 
slotShowSearchResult(const QString & url)509 void Navigator::slotShowSearchResult( const QString &url )
510 {
511   QString u = url;
512   u.replace( QStringLiteral("%k"), mSearchEdit->text() );
513 
514   Q_EMIT itemSelected( u );
515 }
516 
slotSearchFinished()517 void Navigator::slotSearchFinished()
518 {
519   mSearchEdit->setEnabled(true);
520   mSearchEdit->setFocus();
521   QApplication::restoreOverrideCursor();
522 
523   qCDebug(KHC_LOG) << "Search finished.";
524 }
525 
checkSearchEdit()526 void Navigator::checkSearchEdit()
527 {
528   mSearchEdit->setEnabled( mSearchWidget->scopeCount() > 0 && !mIndexingProc );
529 }
530 
531 
hideSearch()532 void Navigator::hideSearch()
533 {
534   mSearchEdit->hide();
535   mTabWidget->removeTab( mTabWidget->indexOf( mSearchWidget ) );
536 }
537 
slotSelectGlossEntry(const QString & id)538 void Navigator::slotSelectGlossEntry( const QString &id )
539 {
540   mGlossaryTree->slotSelectGlossEntry( id );
541 }
542 
homeURL()543 QUrl Navigator::homeURL()
544 {
545   if ( !mHomeUrl.isEmpty() ) return mHomeUrl;
546 
547   KSharedConfig::Ptr cfg = KSharedConfig::openConfig();
548   // We have to reparse the configuration here in order to get a
549   // language-specific StartUrl, e.g. "StartUrl[de]".
550   cfg->reparseConfiguration();
551   mHomeUrl = QUrl(cfg->group("General").readPathEntry( "StartUrl", QStringLiteral("khelpcenter:home") ));
552   return mHomeUrl;
553 }
554 
readConfig()555 void Navigator::readConfig()
556 {
557   if ( Prefs::currentTab() == Prefs::Search ) {
558     mTabWidget->setCurrentIndex( mTabWidget->indexOf( mSearchWidget ) );
559   } else if ( Prefs::currentTab() == Prefs::Glossary ) {
560     mTabWidget->setCurrentIndex( mTabWidget->indexOf( mGlossaryTree ) );
561   } else {
562     mTabWidget->setCurrentIndex( mTabWidget->indexOf( mContentsTree ) );
563   }
564 }
565 
writeConfig()566 void Navigator::writeConfig()
567 {
568   if ( mTabWidget->currentWidget() == mSearchWidget ) {
569     Prefs::setCurrentTab( Prefs::Search );
570   } else if ( mTabWidget->currentWidget() == mGlossaryTree ) {
571     Prefs::setCurrentTab( Prefs::Glossary );
572   } else {
573     Prefs::setCurrentTab( Prefs::Content );
574   }
575 }
576 
clearSearch()577 void Navigator::clearSearch()
578 {
579   mSearchEdit->setText( QString() );
580 }
581 
slotDelayedIndexingStart()582 void Navigator::slotDelayedIndexingStart()
583 {
584   mIndexingQueue.clear();
585 
586   const DocEntry::List &entries = DocMetaInfo::self()->docEntries();
587   for ( DocEntry *entry : entries ) {
588     if ( mSearchEngine->needsIndex( entry ) ) {
589       mIndexingQueue.append( entry );
590     }
591   }
592 
593   if ( mIndexingQueue.isEmpty() ) {
594     return;
595   }
596 
597   Q_EMIT setStatusBarText( i18n( "Updating search index..." ) );
598 
599   mIndexingTimer.start();
600 
601   slotDoIndexWork();
602 }
603 
slotDoIndexWork()604 void Navigator::slotDoIndexWork()
605 {
606   if ( mIndexingQueue.isEmpty() ) {
607     mIndexingTimer.stop();
608     Q_EMIT setStatusBarText( i18n( "Updating search index... done." ) );
609     mIndexingBar->hide();
610     mSearchWidget->searchIndexUpdated();
611     return;
612   }
613 
614   const DocEntry *entry = mIndexingQueue.takeFirst();
615 
616   QString error;
617   SearchHandler *handler = mSearchEngine->handler( entry->documentType() );
618   if ( !handler ) {
619     return slotDoIndexWork();
620   }
621   if ( !handler->checkPaths( &error ) ) {
622     qCWarning(KHC_LOG) << "Indexing path error for" << entry->name() << ":" << error;
623     return slotDoIndexWork();
624   }
625   QString indexer = handler->indexCommand( entry->identifier() );
626   if ( indexer.isEmpty() ) {
627     qCWarning(KHC_LOG) << "Empty indexer for" << entry->identifier() << entry->documentType();
628     return slotDoIndexWork();
629   }
630 
631   const QString indexDir = Prefs::indexDirectory();
632 
633   indexer.replace( QLatin1String( "%i" ), entry->identifier() );
634   indexer.replace( QLatin1String( "%d" ), indexDir );
635   indexer.replace( QLatin1String( "%p" ), entry->url() );
636   qCDebug(KHC_LOG) << "Indexer:" << indexer;
637 
638   if ( !QDir().mkpath( indexDir ) ) {
639     qCWarning(KHC_LOG) << "cannot create the directory:" << indexDir;
640     return slotDoIndexWork();
641   }
642 
643   mIndexingProc = new KProcess;
644 
645   *mIndexingProc << KShell::splitArgs( indexer );
646 
647   connect(mIndexingProc, QOverload<int, QProcess::ExitStatus>::of(&KProcess::finished), this, &Navigator::slotProcessExited);
648 
649   mIndexingProc->start();
650 
651   if ( !mIndexingProc->waitForStarted() )  {
652     qCWarning(KHC_LOG) << "Unable to start command" << indexer;
653     delete mIndexingProc;
654     mIndexingProc = nullptr;
655     return slotDoIndexWork();
656   }
657 }
658 
slotProcessExited(int exitCode,QProcess::ExitStatus exitStatus)659 void Navigator::slotProcessExited( int exitCode, QProcess::ExitStatus exitStatus )
660 {
661   if ( exitStatus != QProcess::NormalExit ) {
662     qCWarning(KHC_LOG) << "Process failed";
663     qCWarning(KHC_LOG) << "stdout output:" << mIndexingProc->readAllStandardOutput();
664     qCWarning(KHC_LOG) << "stderr output:" << mIndexingProc->readAllStandardError();
665   } else if ( exitCode != 0 ) {
666     qCWarning(KHC_LOG) << "running" << mIndexingProc->program() << "failed with exitCode" << exitCode;
667     qCWarning(KHC_LOG) << "stdout output:" << mIndexingProc->readAllStandardOutput();
668     qCWarning(KHC_LOG) << "stderr output:" << mIndexingProc->readAllStandardError();
669   }
670   delete mIndexingProc;
671   mIndexingProc = nullptr;
672 
673   slotDoIndexWork();
674 }
675 
slotShowIndexingProgressBar()676 void Navigator::slotShowIndexingProgressBar()
677 {
678   if ( !mIndexingProc ) {
679     return;
680   }
681 
682   mIndexingBar->setRange( 0, 0 );
683   mIndexingBar->show();
684 }
685 
686 
687 // vim:ts=2:sw=2:et
688