source: trunk/tools/assistant/helpdialogimpl.cpp@ 191

Last change on this file since 191 was 191, checked in by rudi, 14 years ago

Qt Assistant added

File size: 33.6 KB
Line 
1/**********************************************************************
2** Copyright (C) 2000-2007 Trolltech ASA. All rights reserved.
3**
4** This file is part of Qt Assistant.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
12** licenses may use this file in accordance with the Qt Commercial License
13** Agreement provided with the Software.
14**
15** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17**
18** See http://www.trolltech.com/gpl/ for GPL licensing information.
19** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
20** information about Qt Commercial License Agreements.
21**
22** Contact info@trolltech.com if any conditions of this licensing are
23** not clear to you.
24**
25**********************************************************************/
26
27#include "helpdialogimpl.h"
28#include "helpwindow.h"
29#include "topicchooserimpl.h"
30#include "docuparser.h"
31#include "mainwindow.h"
32#include "config.h"
33#include "tabbedbrowser.h"
34
35#include <qaccel.h>
36#include <qapplication.h>
37#include <qcursor.h>
38#include <qdir.h>
39#include <qeventloop.h>
40#include <qfile.h>
41#include <qfileinfo.h>
42#include <qheader.h>
43#include <qlabel.h>
44#include <qlineedit.h>
45#include <qmessagebox.h>
46#include <qpixmap.h>
47#include <qprogressbar.h>
48#include <qptrlist.h>
49#include <qptrstack.h>
50#include <qpushbutton.h>
51#include <qregexp.h>
52#include <qsettings.h>
53#include <qstatusbar.h>
54#include <qtabwidget.h>
55#include <qtextbrowser.h>
56#include <qtextstream.h>
57#include <qtimer.h>
58#include <qurl.h>
59#include <qvalidator.h>
60
61#include <stdlib.h>
62#include <limits.h>
63
64static QString stripAmpersand( const QString &str )
65{
66 QString s( str );
67 s = s.replace( '&', "" );
68 return s;
69}
70
71static bool verifyDirectory(const QString &str)
72{
73 QFileInfo dirInfo(str);
74 if (!dirInfo.exists())
75 return QDir().mkdir(str);
76 if (!dirInfo.isDir()) {
77 qWarning("'%s' exists but is not a directory", str.latin1());
78 return FALSE;
79 }
80 return TRUE;
81}
82
83struct IndexKeyword {
84 IndexKeyword( const QString &kw, const QString &l )
85 : keyword( kw ), link( l ) {}
86 IndexKeyword() : keyword( QString::null ), link( QString::null ) {}
87 bool operator<( const IndexKeyword &ik ) const {
88 return keyword.lower() < ik.keyword.lower();
89 }
90 bool operator<=( const IndexKeyword &ik ) const {
91 return keyword.lower() <= ik.keyword.lower();
92 }
93 bool operator>( const IndexKeyword &ik ) const {
94 return keyword.lower() > ik.keyword.lower();
95 }
96 Q_DUMMY_COMPARISON_OPERATOR( IndexKeyword )
97 QString keyword;
98 QString link;
99};
100
101QDataStream &operator>>( QDataStream &s, IndexKeyword &ik )
102{
103 s >> ik.keyword;
104 s >> ik.link;
105 return s;
106}
107
108QDataStream &operator<<( QDataStream &s, const IndexKeyword &ik )
109{
110 s << ik.keyword;
111 s << ik.link;
112 return s;
113}
114
115QValidator::State SearchValidator::validate( QString &str, int & ) const
116{
117 for ( int i = 0; i < (int) str.length(); ++i ) {
118 QChar c = str[i];
119 if ( !c.isLetterOrNumber() && c != '\'' && c != '`'
120 && c != '\"' && c != ' ' && c != '-' && c != '_'
121 && c!= '*' )
122 return QValidator::Invalid;
123 }
124 return QValidator::Acceptable;
125}
126
127HelpNavigationListItem::HelpNavigationListItem( QListBox *ls, const QString &txt )
128 : QListBoxText( ls, txt )
129{
130}
131
132void HelpNavigationListItem::addLink( const QString &link )
133{
134 int hash = link.find( '#' );
135 if ( hash == -1 ) {
136 linkList << link;
137 return;
138 }
139
140 QString preHash = link.left( hash );
141 if ( linkList.grep( preHash, FALSE ).count() > 0 )
142 return;
143 linkList << link;
144}
145
146HelpNavigationContentsItem::HelpNavigationContentsItem( QListView *v, QListViewItem *after )
147 : QListViewItem( v, after )
148{
149}
150
151HelpNavigationContentsItem::HelpNavigationContentsItem( QListViewItem *v, QListViewItem *after )
152 : QListViewItem( v, after )
153{
154}
155
156void HelpNavigationContentsItem::setLink( const QString &lnk )
157{
158 theLink = lnk;
159}
160
161QString HelpNavigationContentsItem::link() const
162{
163 return theLink;
164}
165
166
167
168HelpDialog::HelpDialog( QWidget *parent, MainWindow *h )
169 : HelpDialogBase( parent, 0, FALSE ), lwClosed( FALSE ), help( h )
170{
171}
172
173void HelpDialog::initialize()
174{
175 connect( tabWidget, SIGNAL( selected(const QString&) ),
176 this, SLOT( currentTabChanged(const QString&) ) );
177 connect( listContents, SIGNAL( mouseButtonClicked(int, QListViewItem*, const QPoint &, int ) ),
178 this, SLOT( showTopic(int,QListViewItem*, const QPoint &) ) );
179 connect( listContents, SIGNAL( currentChanged(QListViewItem*) ),
180 this, SLOT( currentContentsChanged(QListViewItem*) ) );
181 connect( listContents, SIGNAL( selectionChanged(QListViewItem*) ),
182 this, SLOT( currentContentsChanged(QListViewItem*) ) );
183 connect( listContents, SIGNAL( doubleClicked(QListViewItem*) ),
184 this, SLOT( showTopic(QListViewItem*) ) );
185 connect( listContents, SIGNAL( returnPressed(QListViewItem*) ),
186 this, SLOT( showTopic(QListViewItem*) ) );
187 connect( listContents, SIGNAL( contextMenuRequested( QListViewItem*, const QPoint&, int ) ),
188 this, SLOT( showItemMenu( QListViewItem*, const QPoint& ) ) );
189 connect( editIndex, SIGNAL( returnPressed() ),
190 this, SLOT( showTopic() ) );
191 connect( editIndex, SIGNAL( textChanged(const QString&) ),
192 this, SLOT( searchInIndex(const QString&) ) );
193
194 connect( listIndex, SIGNAL( selectionChanged(QListBoxItem*) ),
195 this, SLOT( currentIndexChanged(QListBoxItem*) ) );
196 connect( listIndex, SIGNAL( returnPressed(QListBoxItem*) ),
197 this, SLOT( showTopic() ) );
198 connect( listIndex, SIGNAL( mouseButtonClicked(int, QListBoxItem*, const QPoint &) ),
199 this, SLOT( showTopic( int, QListBoxItem *, const QPoint & ) ) );
200 connect( listIndex, SIGNAL( currentChanged(QListBoxItem*) ),
201 this, SLOT( currentIndexChanged(QListBoxItem*) ) );
202 connect( listIndex, SIGNAL( contextMenuRequested( QListBoxItem*, const QPoint& ) ),
203 this, SLOT( showItemMenu( QListBoxItem*, const QPoint& ) ) );
204
205 connect( listBookmarks, SIGNAL( mouseButtonClicked(int, QListViewItem*, const QPoint&, int ) ),
206 this, SLOT( showTopic(int, QListViewItem*, const QPoint &) ) );
207 connect( listBookmarks, SIGNAL( returnPressed(QListViewItem*) ),
208 this, SLOT( showTopic(QListViewItem*) ) );
209 connect( listBookmarks, SIGNAL( selectionChanged(QListViewItem*) ),
210 this, SLOT( currentBookmarkChanged(QListViewItem*) ) );
211 connect( listBookmarks, SIGNAL( currentChanged(QListViewItem*) ),
212 this, SLOT( currentBookmarkChanged(QListViewItem*) ) );
213 connect( listBookmarks, SIGNAL( contextMenuRequested( QListViewItem*, const QPoint&, int ) ),
214 this, SLOT( showItemMenu( QListViewItem*, const QPoint& ) ) );
215 connect( resultBox, SIGNAL( contextMenuRequested( QListBoxItem*, const QPoint& ) ),
216 this, SLOT( showItemMenu( QListBoxItem*, const QPoint& ) ) );
217
218 cacheFilesPath = QDir::homeDirPath() + "/.assistant/"; //### Find a better location for the dbs
219
220 editIndex->installEventFilter( this );
221 listBookmarks->header()->hide();
222 listBookmarks->header()->setStretchEnabled( TRUE );
223 listContents->header()->hide();
224 listContents->header()->setStretchEnabled( TRUE );
225 framePrepare->hide();
226 connect( qApp, SIGNAL(lastWindowClosed()), SLOT(lastWinClosed()) );
227
228 termsEdit->setValidator( new SearchValidator( termsEdit ) );
229
230 itemPopup = new QPopupMenu( this );
231 itemPopup->insertItem( tr( "Open Link in Current Tab" ), 0 );
232 itemPopup->insertItem( tr( "Open Link in New Window" ), 1 );
233 itemPopup->insertItem( tr( "Open Link in New Tab" ), 2 );
234
235 contentList.setAutoDelete( TRUE );
236 contentList.clear();
237
238 initDoneMsgShown = FALSE;
239 fullTextIndex = 0;
240 indexDone = FALSE;
241 titleMapDone = FALSE;
242 contentsInserted = FALSE;
243 bookmarksInserted = FALSE;
244 setupTitleMap();
245}
246
247
248void HelpDialog::processEvents()
249{
250 qApp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput );
251}
252
253
254void HelpDialog::lastWinClosed()
255{
256 lwClosed = TRUE;
257}
258
259
260void HelpDialog::removeOldCacheFiles()
261{
262 QString dir = cacheFilesPath; // ### remove the last '/' ?
263 if (!verifyDirectory(cacheFilesPath)) {
264 qWarning( "Failed to created assistant directory" );
265 return;
266 }
267 QString pname = "." + Config::configuration()->profileName();
268
269 QStringList fileList;
270 fileList << "indexdb" << "indexdb.dict" << "indexdb.doc" << "contentdb";
271 QStringList::iterator it = fileList.begin();
272 for ( ; it != fileList.end(); ++it ) {
273 if ( QFile::exists( cacheFilesPath + *it + pname ) ) {
274 QFile f( cacheFilesPath + *it + pname );
275 f.remove();
276 }
277 }
278}
279
280void HelpDialog::timerEvent(QTimerEvent *e)
281{
282 static int opacity = 255;
283 // To allow patching of 3.2.3 assistant for qsa.
284#if QT_VERSION >= 0x030300
285 help->setWindowOpacity((opacity-=4)/255.0);
286 if (opacity<=0)
287#endif
288 qApp->quit();
289}
290
291
292void HelpDialog::loadIndexFile()
293{
294 if ( indexDone )
295 return;
296
297 setCursor( waitCursor );
298 indexDone = TRUE;
299 labelPrepare->setText( tr( "Prepare..." ) );
300 framePrepare->show();
301 processEvents();
302
303 QProgressBar *bar = progressPrepare;
304 bar->setTotalSteps( 100 );
305 bar->setProgress( 0 );
306
307 keywordDocuments.clear();
308 QValueList<IndexKeyword> lst;
309 QFile indexFile( cacheFilesPath + "indexdb." +
310 Config::configuration()->profileName() );
311 if ( !indexFile.open( IO_ReadOnly ) ) {
312 buildKeywordDB();
313 processEvents();
314 if( lwClosed )
315 return;
316 if (!indexFile.open(IO_ReadOnly)) {
317 QMessageBox::warning(help, tr("Qt Assistant"), tr("Failed to load keyword index file\n"
318 "Assistant will not work!"));
319#if defined Q_WS_WIN || defined Q_WS_MACX
320 startTimer(50);
321#endif
322 return;
323 }
324 }
325
326 editIndex->setEnabled(FALSE);
327
328 QDataStream ds( &indexFile );
329 Q_UINT32 fileAges;
330 ds >> fileAges;
331 if ( fileAges != getFileAges() ) {
332 indexFile.close();
333 buildKeywordDB();
334 if ( !indexFile.open( IO_ReadOnly ) ) {
335 QMessageBox::warning( help, tr( "Qt Assistant" ),
336 tr( "Cannot open the index file %1" ).arg( QFileInfo( indexFile ).absFilePath() ) );
337 editIndex->setEnabled(TRUE);
338 return;
339 }
340 ds.setDevice( &indexFile );
341 ds >> fileAges;
342 }
343 ds >> lst;
344 indexFile.close();
345
346 bar->setProgress( bar->totalSteps() );
347 processEvents();
348
349 listIndex->clear();
350 HelpNavigationListItem *lastItem = 0;
351 QString lastKeyword = QString::null;
352 QValueList<IndexKeyword>::ConstIterator it = lst.begin();
353 for ( ; it != lst.end(); ++it ) {
354 if ( lastKeyword.lower() != (*it).keyword.lower() )
355 lastItem = new HelpNavigationListItem( listIndex, (*it).keyword );
356 lastItem->addLink( (*it).link );
357 lastKeyword = (*it).keyword;
358
359 QString lnk = (*it).link;
360 int i = lnk.findRev('#');
361 if ( i > -1 )
362 lnk = lnk.left( i );
363 if (!keywordDocuments.contains(lnk))
364 keywordDocuments.append(lnk);
365 }
366 framePrepare->hide();
367 showInitDoneMessage();
368 setCursor( arrowCursor );
369 editIndex->setEnabled(TRUE);
370}
371
372Q_UINT32 HelpDialog::getFileAges()
373{
374 QStringList addDocuFiles = Config::configuration()->docFiles();
375 QStringList::const_iterator i = addDocuFiles.begin();
376
377 Q_UINT32 fileAges = 0;
378 for( ; i != addDocuFiles.end(); ++i ) {
379 QFileInfo fi( *i );
380 if ( fi.exists() )
381 fileAges += fi.lastModified().toTime_t();
382 }
383
384 return fileAges;
385}
386
387void HelpDialog::buildKeywordDB()
388{
389 QStringList addDocuFiles = Config::configuration()->docFiles();
390 QStringList::iterator i = addDocuFiles.begin();
391
392 int steps = 0;
393 for( ; i != addDocuFiles.end(); i++ )
394 steps += QFileInfo( *i ).size();
395
396 labelPrepare->setText( tr( "Prepare..." ) );
397 progressPrepare->setTotalSteps( steps );
398 progressPrepare->setProgress( 0 );
399 processEvents();
400
401 QValueList<IndexKeyword> lst;
402 Q_UINT32 fileAges = 0;
403 for( i = addDocuFiles.begin(); i != addDocuFiles.end(); i++ ){
404 QFile file( *i );
405 if ( !file.exists() ) {
406 QMessageBox::warning( this, tr( "Warning" ),
407 tr( "Documentation file %1 does not exist!\n"
408 "Skipping file." ).arg( QFileInfo( file ).absFilePath() ) );
409 continue;
410 }
411 fileAges += QFileInfo( file ).lastModified().toTime_t();
412 DocuParser *handler = DocuParser::createParser( *i );
413 bool ok = handler->parse( &file );
414 file.close();
415 if( !ok ){
416 QString msg = QString( "In file %1:\n%2" )
417 .arg( QFileInfo( file ).absFilePath() )
418 .arg( handler->errorProtocol() );
419 QMessageBox::critical( this, tr( "Parse Error" ), tr( msg ) );
420 delete handler;
421 continue;
422 }
423
424 QPtrList<IndexItem> indLst = handler->getIndexItems();
425 QPtrListIterator<IndexItem> it( indLst );
426 IndexItem *indItem;
427 int counter = 0;
428 while ( ( indItem = it.current() ) != 0 ) {
429 QFileInfo fi( indItem->reference );
430 lst.append( IndexKeyword( indItem->keyword, fi.absFilePath() ) );
431 if ( progressPrepare )
432 progressPrepare->setProgress( progressPrepare->progress() +
433 int(fi.absFilePath().length() * 1.6) );
434
435 if( ++counter%100 == 0 ) {
436 processEvents();
437 if( lwClosed ) {
438 return;
439 }
440 }
441 ++it;
442 }
443 delete handler;
444 }
445 if ( !lst.isEmpty() )
446 qHeapSort( lst );
447
448 QFile indexout( cacheFilesPath + "indexdb." + Config::configuration()->profileName() );
449 if ( verifyDirectory(cacheFilesPath) && indexout.open( IO_WriteOnly ) ) {
450 QDataStream s( &indexout );
451 s << fileAges;
452 s << lst;
453 indexout.close();
454 }
455}
456
457void HelpDialog::setupTitleMap()
458{
459 if ( titleMapDone )
460 return;
461 if ( Config::configuration()->docRebuild() ) {
462 removeOldCacheFiles();
463 Config::configuration()->setDocRebuild( FALSE );
464 Config::configuration()->saveProfile( Config::configuration()->profile() );
465 }
466 if ( contentList.isEmpty() )
467 getAllContents();
468
469 titleMapDone = TRUE;
470 titleMap.clear();
471 QDictIterator<ContentList> lstIt( contentList );
472 for ( ; lstIt.current(); ++lstIt ) {
473 QValueList<ContentItem> &lst = *(lstIt.current());
474 QValueListConstIterator<ContentItem> it;
475 for ( it = lst.begin(); it != lst.end(); ++it ) {
476 QFileInfo link( (*it).reference.simplifyWhiteSpace() );
477 titleMap[ link.absFilePath() ] = (*it).title.stripWhiteSpace();
478 }
479 }
480 processEvents();
481}
482
483void HelpDialog::getAllContents()
484{
485 QFile contentFile( cacheFilesPath + "contentdb." + Config::configuration()->profileName() );
486 contentList.clear();
487 if ( !contentFile.open( IO_ReadOnly ) ) {
488 buildContentDict();
489 return;
490 }
491
492 QDataStream ds( &contentFile );
493 Q_UINT32 fileAges;
494 ds >> fileAges;
495 if ( fileAges != getFileAges() ) {
496 contentFile.close();
497 buildContentDict();
498 return;
499 }
500 QString key;
501 QValueList<ContentItem> lst;
502 while ( !ds.atEnd() ) {
503 ds >> key;
504 ds >> lst;
505 contentList.insert( key, new QValueList<ContentItem>( lst ) );
506 }
507 contentFile.close();
508 processEvents();
509
510}
511
512void HelpDialog::buildContentDict()
513{
514 QStringList docuFiles = Config::configuration()->docFiles();
515
516 Q_UINT32 fileAges = 0;
517 for( QStringList::iterator it = docuFiles.begin(); it != docuFiles.end(); it++ ) {
518 QFile file( *it );
519 if ( !file.exists() ) {
520 QMessageBox::warning( this, tr( "Warning" ),
521 tr( "Documentation file %1 does not exist!\n"
522 "Skipping file." ).arg( QFileInfo( file ).absFilePath() ) );
523 continue;
524 }
525 fileAges += QFileInfo( file ).lastModified().toTime_t();
526 DocuParser *handler = DocuParser::createParser( *it );
527 if( !handler ) {
528 QMessageBox::warning( this, tr( "Warning" ),
529 tr( "Documentation file %1 is not compatible!\n"
530 "Skipping file." ).arg( QFileInfo( file ).absFilePath() ) );
531 continue;
532 }
533 bool ok = handler->parse( &file );
534 file.close();
535 if( ok ) {
536 contentList.insert( *it, new QValueList<ContentItem>( handler->getContentItems() ) );
537 delete handler;
538 } else {
539 QString msg = QString( "In file %1:\n%2" )
540 .arg( QFileInfo( file ).absFilePath() )
541 .arg( handler->errorProtocol() );
542 QMessageBox::critical( this, tr( "Parse Error" ), tr( msg ) );
543 continue;
544 }
545 }
546
547 QString pname = Config::configuration()->profileName();
548 QFile contentOut( cacheFilesPath + "contentdb." + pname );
549 QFile::remove( cacheFilesPath + "indexdb.dict." + pname );
550 QFile::remove( cacheFilesPath + "indexdb.doc." + pname );
551 if ( contentOut.open( IO_WriteOnly ) ) {
552 QDataStream s( &contentOut );
553 s << fileAges;
554 QDictIterator<ContentList> it( contentList );
555 for ( ; it.current(); ++it ) {
556 s << it.currentKey();
557 s << *(it.current());
558 }
559 contentOut.close();
560 }
561}
562
563void HelpDialog::currentTabChanged( const QString &s )
564{
565 if ( stripAmpersand( s ).contains( tr( "Index" ) ) )
566 QTimer::singleShot( 0, this, SLOT( loadIndexFile() ) );
567 else if ( stripAmpersand( s ).contains( tr( "Bookmarks" ) ) )
568 insertBookmarks();
569 else if ( stripAmpersand( s ).contains( tr( "Contents" ) ) )
570 QTimer::singleShot( 0, this, SLOT( insertContents() ) );
571 else if ( stripAmpersand( s ).contains( tr( "Search" ) ) )
572 QTimer::singleShot( 0, this, SLOT( setupFullTextIndex() ) );
573}
574
575void HelpDialog::showInitDoneMessage()
576{
577 if ( initDoneMsgShown )
578 return;
579 initDoneMsgShown = TRUE;
580 help->statusBar()->message( tr( "Done" ), 3000 );
581}
582
583void HelpDialog::currentIndexChanged( QListBoxItem * )
584{
585}
586
587
588void HelpDialog::showTopic( int button, QListBoxItem *item,
589 const QPoint & )
590{
591 if( button == LeftButton && item )
592 showTopic();
593}
594
595void HelpDialog::showTopic( int button, QListViewItem *item,
596 const QPoint & )
597{
598 if( button == LeftButton && item )
599 showTopic();
600}
601
602void HelpDialog::showTopic( QListViewItem *item )
603{
604 if( item )
605 showTopic();
606}
607
608void HelpDialog::showTopic()
609{
610 if ( stripAmpersand( tabWidget->tabLabel( tabWidget->currentPage() ) ).contains( tr( "Index" ) ) )
611 showIndexTopic();
612 else if ( stripAmpersand( tabWidget->tabLabel( tabWidget->currentPage() ) ).contains( tr( "Bookmarks" ) ) )
613 showBookmarkTopic();
614 else if ( stripAmpersand( tabWidget->tabLabel( tabWidget->currentPage() ) ).contains( tr( "Contents" ) ) )
615 showContentsTopic();
616}
617
618void HelpDialog::showIndexTopic()
619{
620 QListBoxItem *i = listIndex->item( listIndex->currentItem() );
621 if ( !i )
622 return;
623
624 editIndex->blockSignals( TRUE );
625 editIndex->setText( i->text() );
626 editIndex->blockSignals( FALSE );
627
628 HelpNavigationListItem *item = (HelpNavigationListItem*)i;
629
630 QStringList links = item->links();
631 if ( links.count() == 1 ) {
632 emit showLink( links.first() );
633 } else {
634 qHeapSort( links );
635 QStringList::Iterator it = links.begin();
636 QStringList linkList;
637 QStringList linkNames;
638 for ( ; it != links.end(); ++it ) {
639 linkList << *it;
640 linkNames << titleOfLink( *it );
641 }
642 QString link = TopicChooser::getLink( this, linkNames, linkList, i->text() );
643 if ( !link.isEmpty() )
644 emit showLink( link );
645 }
646}
647
648void HelpDialog::searchInIndex( const QString &s )
649{
650 QListBoxItem *i = listIndex->firstItem();
651 QString sl = s.lower();
652 while ( i ) {
653 QString t = i->text();
654 if ( t.length() >= sl.length() &&
655 i->text().left(s.length()).lower() == sl ) {
656 listIndex->setCurrentItem( i );
657 listIndex->setTopItem(listIndex->index(i));
658 break;
659 }
660 i = i->next();
661 }
662}
663
664QString HelpDialog::titleOfLink( const QString &link )
665{
666 QString s( link );
667 s.remove( s.find( '#' ), s.length() );
668 s = titleMap[ s ];
669 if ( s.isEmpty() )
670 return link;
671 return s;
672}
673
674bool HelpDialog::eventFilter( QObject * o, QEvent * e )
675{
676 if ( !o || !e )
677 return TRUE;
678
679 if ( o == editIndex && e->type() == QEvent::KeyPress ) {
680 QKeyEvent *ke = (QKeyEvent*)e;
681 if ( ke->key() == Key_Up ) {
682 int i = listIndex->currentItem();
683 if ( --i >= 0 ) {
684 listIndex->setCurrentItem( i );
685 editIndex->blockSignals( TRUE );
686 editIndex->setText( listIndex->currentText() );
687 editIndex->blockSignals( FALSE );
688 }
689 return TRUE;
690 } else if ( ke->key() == Key_Down ) {
691 int i = listIndex->currentItem();
692 if ( ++i < int(listIndex->count()) ) {
693 listIndex->setCurrentItem( i );
694 editIndex->blockSignals( TRUE );
695 editIndex->setText( listIndex->currentText() );
696 editIndex->blockSignals( FALSE );
697 }
698 return TRUE;
699 } else if ( ke->key() == Key_Next || ke->key() == Key_Prior ) {
700 QApplication::sendEvent( listIndex, e);
701 editIndex->blockSignals( TRUE );
702 editIndex->setText( listIndex->currentText() );
703 editIndex->blockSignals( FALSE );
704 }
705 }
706
707 return QWidget::eventFilter( o, e );
708}
709
710void HelpDialog::addBookmark()
711{
712 if ( !bookmarksInserted )
713 insertBookmarks();
714 QString link = QUrl( help->browsers()->currentBrowser()->context(),
715 help->browsers()->currentBrowser()->source() ).path();
716 QString title = help->browsers()->currentBrowser()->documentTitle();
717 if ( title.isEmpty() )
718 title = titleOfLink( link );
719 HelpNavigationContentsItem *i = new HelpNavigationContentsItem( listBookmarks, 0 );
720 i->setText( 0, title );
721 i->setLink( link );
722 saveBookmarks();
723 help->updateBookmarkMenu();
724}
725
726void HelpDialog::removeBookmark()
727{
728 if ( !listBookmarks->currentItem() )
729 return;
730
731 delete listBookmarks->currentItem();
732 saveBookmarks();
733 if ( listBookmarks->firstChild() ) {
734 listBookmarks->setSelected( listBookmarks->firstChild(), TRUE );
735 }
736 help->updateBookmarkMenu();
737}
738
739void HelpDialog::insertBookmarks()
740{
741 if ( bookmarksInserted )
742 return;
743 bookmarksInserted = TRUE;
744 listBookmarks->clear();
745 QFile f( cacheFilesPath + "bookmarks." + Config::configuration()->profileName() );
746 if ( !f.open( IO_ReadOnly ) )
747 return;
748 QTextStream ts( &f );
749 while ( !ts.atEnd() ) {
750 HelpNavigationContentsItem *i = new HelpNavigationContentsItem( listBookmarks, 0 );
751 i->setText( 0, ts.readLine() );
752 i->setLink( ts.readLine() );
753 }
754 help->updateBookmarkMenu();
755 showInitDoneMessage();
756}
757
758void HelpDialog::currentBookmarkChanged( QListViewItem * )
759{
760}
761
762void HelpDialog::showBookmarkTopic()
763{
764 if ( !listBookmarks->currentItem() )
765 return;
766
767 HelpNavigationContentsItem *i = (HelpNavigationContentsItem*)listBookmarks->currentItem();
768 QString absPath = "";
769 if ( QFileInfo( i->link() ).isRelative() )
770 absPath = documentationPath + "/";
771 emit showLink( absPath + i->link() );
772}
773
774void HelpDialog::saveBookmarks()
775{
776 QFile f( cacheFilesPath + "bookmarks." + Config::configuration()->profileName() );
777 if ( !f.open( IO_WriteOnly ) )
778 return;
779 QTextStream ts( &f );
780 QListViewItemIterator it( listBookmarks );
781 for ( ; it.current(); ++it ) {
782 HelpNavigationContentsItem *i = (HelpNavigationContentsItem*)it.current();
783 ts << i->text( 0 ) << endl;
784 ts << i->link() << endl;
785 }
786 f.close();
787}
788
789void HelpDialog::insertContents()
790{
791 if ( contentsInserted )
792 return;
793
794 if ( contentList.isEmpty() )
795 getAllContents();
796
797 contentsInserted = TRUE;
798 listContents->clear();
799 setCursor( waitCursor );
800 if ( !titleMapDone )
801 setupTitleMap();
802
803 listContents->setSorting( -1 );
804
805 QDictIterator<ContentList> lstIt( contentList );
806 for ( ; lstIt.current(); ++lstIt ) {
807 HelpNavigationContentsItem *newEntry;
808
809 HelpNavigationContentsItem *contentEntry = 0;
810 QPtrStack<HelpNavigationContentsItem> stack;
811 stack.clear();
812 int depth = 0;
813 bool root = FALSE;
814
815 HelpNavigationContentsItem *lastItem[64];
816 for( int j = 0; j < 64; ++j )
817 lastItem[j] = 0;
818
819
820 QValueList<ContentItem> &lst = *(lstIt.current());
821 QValueListConstIterator<ContentItem> it;
822 for( it = lst.begin(); it != lst.end(); ++it ){
823 ContentItem item = *it;
824 if( item.depth == 0 ){
825 newEntry = new HelpNavigationContentsItem( listContents, 0 );
826 newEntry->setPixmap( 0, QPixmap::fromMimeSource( "book.png" ) );
827 newEntry->setText( 0, item.title );
828 newEntry->setLink( item.reference );
829 stack.push( newEntry );
830 depth = 1;
831 root = TRUE;
832 }
833 else{
834 if( (item.depth > depth) && root ) {
835 depth = item.depth;
836 stack.push( contentEntry );
837 }
838 if( item.depth == depth ) {
839 contentEntry = new HelpNavigationContentsItem( stack.top(), lastItem[ depth ] );
840 lastItem[ depth ] = contentEntry;
841 contentEntry->setText( 0, item.title );
842 contentEntry->setLink( item.reference );
843 }
844 else if( item.depth < depth ) {
845 stack.pop();
846 depth--;
847 item = *(--it);
848 }
849 }
850 }
851 processEvents();
852 }
853 setCursor( arrowCursor );
854 showInitDoneMessage();
855}
856
857void HelpDialog::currentContentsChanged( QListViewItem * )
858{
859}
860
861void HelpDialog::showContentsTopic()
862{
863 HelpNavigationContentsItem *i = (HelpNavigationContentsItem*)listContents->currentItem();
864 if ( !i )
865 return;
866 emit showLink( i->link() );
867}
868
869void HelpDialog::toggleContents()
870{
871 if ( !isVisible() || tabWidget->currentPageIndex() != 0 ) {
872 tabWidget->setCurrentPage( 0 );
873 parentWidget()->show();
874 }
875 else
876 parentWidget()->hide();
877}
878
879void HelpDialog::toggleIndex()
880{
881 if ( !isVisible() || tabWidget->currentPageIndex() != 1 || !editIndex->hasFocus() ) {
882 tabWidget->setCurrentPage( 1 );
883 parentWidget()->show();
884 editIndex->setFocus();
885 }
886 else
887 parentWidget()->hide();
888}
889
890void HelpDialog::toggleBookmarks()
891{
892 if ( !isVisible() || tabWidget->currentPageIndex() != 2 ) {
893 tabWidget->setCurrentPage( 2 );
894 parentWidget()->show();
895 }
896 else
897 parentWidget()->hide();
898}
899
900void HelpDialog::toggleSearch()
901{
902 if ( !isVisible() || tabWidget->currentPageIndex() != 3 ) {
903 tabWidget->setCurrentPage( 3 );
904 parentWidget()->show();
905 }
906 else
907 parentWidget()->hide();
908}
909
910void HelpDialog::setupFullTextIndex()
911{
912 if ( fullTextIndex )
913 return;
914
915 QStringList documentList;
916 QString pname = Config::configuration()->profileName();
917 fullTextIndex = new Index( documentList, QDir::homeDirPath() ); // ### Is this correct ?
918 if (!verifyDirectory(cacheFilesPath)) {
919 QMessageBox::warning(help, tr("Qt Assistant"),
920 tr("Failed to save fulltext search index\n"
921 "Assistant will not work!"));
922 return;
923 }
924 searchButton->setEnabled(FALSE);
925 helpButton->setEnabled(FALSE);
926 termsEdit->setEnabled(FALSE);
927
928 fullTextIndex->setDictionaryFile( cacheFilesPath + "indexdb.dict." + pname );
929 fullTextIndex->setDocListFile( cacheFilesPath + "indexdb.doc." + pname );
930 processEvents();
931
932 connect( fullTextIndex, SIGNAL( indexingProgress( int ) ),
933 this, SLOT( setIndexingProgress( int ) ) );
934 QFile f( cacheFilesPath + "indexdb.dict." + pname );
935 if ( !f.exists() ) {
936 QMap<QString, QString>::ConstIterator it = titleMap.begin();
937 QString documentName;
938 for ( ; it != titleMap.end(); ++it ) {
939 documentName = it.key();
940 int i = documentName.findRev('#');
941 if ( i > -1 )
942 documentName = documentName.left( i );
943
944 if (!documentList.contains(documentName))
945 documentList << documentName;
946 }
947 loadIndexFile();
948 for (QStringList::Iterator it2 = keywordDocuments.begin(); it2 != keywordDocuments.end(); ++it2) {
949 if (!documentList.contains(*it2))
950 documentList << *it2;
951 }
952 fullTextIndex->setDocList( documentList );
953
954 help->statusBar()->clear();
955 setCursor( waitCursor );
956 labelPrepare->setText( tr( "Indexing files..." ) );
957 progressPrepare->setTotalSteps( 100 );
958 progressPrepare->reset();
959 progressPrepare->show();
960 framePrepare->show();
961 processEvents();
962 if ( fullTextIndex->makeIndex() != -1 ) {
963 fullTextIndex->writeDict();
964 progressPrepare->setProgress( 100 );
965 framePrepare->hide();
966 setCursor( arrowCursor );
967 showInitDoneMessage();
968 keywordDocuments.clear();
969 }
970 } else {
971 setCursor( waitCursor );
972 help->statusBar()->message( tr( "Reading dictionary..." ) );
973 processEvents();
974 fullTextIndex->readDict();
975 help->statusBar()->message( tr( "Done" ), 3000 );
976 setCursor( arrowCursor );
977 }
978 searchButton->setEnabled(TRUE);
979 termsEdit->setEnabled(TRUE);
980 helpButton->setEnabled(TRUE);
981}
982
983void HelpDialog::setIndexingProgress( int prog )
984{
985 progressPrepare->setProgress( prog );
986 processEvents();
987}
988
989void HelpDialog::startSearch()
990{
991 QString str = termsEdit->text();
992 str = str.replace( "\'", "\"" );
993 str = str.replace( "`", "\"" );
994 QString buf = str;
995 str = str.replace( "-", " " );
996 str = str.replace( QRegExp( "\\s[\\S]?\\s" ), " " );
997 terms = QStringList::split( " ", str );
998 QStringList termSeq;
999 QStringList seqWords;
1000 QStringList::iterator it = terms.begin();
1001 for ( ; it != terms.end(); ++it ) {
1002 (*it) = (*it).simplifyWhiteSpace();
1003 (*it) = (*it).lower();
1004 (*it) = (*it).replace( "\"", "" );
1005 }
1006 if ( str.contains( '\"' ) ) {
1007 if ( (str.contains( '\"' ))%2 == 0 ) {
1008 int beg = 0;
1009 int end = 0;
1010 QString s;
1011 beg = str.find( '\"', beg );
1012 while ( beg != -1 ) {
1013 beg++;
1014 end = str.find( '\"', beg );
1015 s = str.mid( beg, end - beg );
1016 s = s.lower();
1017 s = s.simplifyWhiteSpace();
1018 if ( s.contains( '*' ) ) {
1019 QMessageBox::warning( this, tr( "Full Text Search" ),
1020 tr( "Using a wildcard within phrases is not allowed." ) );
1021 return;
1022 }
1023 seqWords += QStringList::split( ' ', s );
1024 termSeq << s;
1025 beg = str.find( '\"', end + 1);
1026 }
1027 } else {
1028 QMessageBox::warning( this, tr( "Full Text Search" ),
1029 tr( "The closing quotation mark is missing." ) );
1030 return;
1031 }
1032 }
1033 setCursor( waitCursor );
1034 foundDocs.clear();
1035 foundDocs = fullTextIndex->query( terms, termSeq, seqWords );
1036 QString msg( QString( "%1 documents found." ).arg( foundDocs.count() ) );
1037 help->statusBar()->message( tr( msg ), 3000 );
1038 resultBox->clear();
1039 for ( it = foundDocs.begin(); it != foundDocs.end(); ++it )
1040 resultBox->insertItem( fullTextIndex->getDocumentTitle( *it ) );
1041
1042 terms.clear();
1043 bool isPhrase = FALSE;
1044 QString s = "";
1045 for ( int i = 0; i < (int)buf.length(); ++i ) {
1046 if ( buf[i] == '\"' ) {
1047 isPhrase = !isPhrase;
1048 s = s.simplifyWhiteSpace();
1049 if ( !s.isEmpty() )
1050 terms << s;
1051 s = "";
1052 } else if ( buf[i] == ' ' && !isPhrase ) {
1053 s = s.simplifyWhiteSpace();
1054 if ( !s.isEmpty() )
1055 terms << s;
1056 s = "";
1057 } else
1058 s += buf[i];
1059 }
1060 if ( !s.isEmpty() )
1061 terms << s;
1062
1063 setCursor( arrowCursor );
1064}
1065
1066void HelpDialog::showSearchHelp()
1067{
1068 emit showLink( Config::configuration()->assistantDocPath() + "/assistant-5.html" );
1069}
1070
1071void HelpDialog::showResultPage( int button, QListBoxItem *i, const QPoint & )
1072{
1073 if( button == LeftButton ) {
1074 showResultPage( i );
1075 }
1076}
1077
1078void HelpDialog::showResultPage( QListBoxItem *i )
1079{
1080 if( !i )
1081 return;
1082 emit showSearchLink( foundDocs[resultBox->index( i )], terms );
1083}
1084
1085void HelpDialog::showItemMenu( QListBoxItem *item, const QPoint &pos )
1086{
1087 if ( !item )
1088 return;
1089 int id = itemPopup->exec( pos );
1090 if ( id == 0 ) {
1091 if ( stripAmpersand( tabWidget->tabLabel( tabWidget->currentPage() ) ).contains( tr( "Index" ) ) )
1092 showTopic();
1093 else {
1094 showResultPage( item );
1095 }
1096 } else if ( id > 0 ) {
1097 HelpWindow *hw = help->browsers()->currentBrowser();
1098 if ( stripAmpersand( tabWidget->tabLabel( tabWidget->currentPage() ) ).contains( tr( "Index" ) ) ) {
1099 editIndex->blockSignals( TRUE );
1100 editIndex->setText( item->text() );
1101 editIndex->blockSignals( FALSE );
1102
1103 HelpNavigationListItem *hi = (HelpNavigationListItem*)item;
1104
1105 QStringList links = hi->links();
1106 if ( links.count() == 1 ) {
1107 if ( id == 1 )
1108 hw->openLinkInNewWindow( links.first() );
1109 else
1110 hw->openLinkInNewPage( links.first() );
1111 } else {
1112 QStringList::Iterator it = links.begin();
1113 QStringList linkList;
1114 QStringList linkNames;
1115 for ( ; it != links.end(); ++it ) {
1116 linkList << *it;
1117 linkNames << titleOfLink( *it );
1118 }
1119 QString link = TopicChooser::getLink( this, linkNames, linkList, item->text() );
1120 if ( !link.isEmpty() ) {
1121 if ( id == 1 )
1122 hw->openLinkInNewWindow( link );
1123 else
1124 hw->openLinkInNewPage( link );
1125 }
1126 }
1127 } else {
1128 QString link = foundDocs[ resultBox->index( item ) ];
1129 if ( id == 1 )
1130 hw->openLinkInNewWindow( link );
1131 else
1132 hw->openLinkInNewPage( link );
1133 }
1134 }
1135}
1136
1137void HelpDialog::showItemMenu( QListViewItem *item, const QPoint &pos )
1138{
1139 if ( !item )
1140 return;
1141 int id = itemPopup->exec( pos );
1142 if ( id == 0 ) {
1143 if ( stripAmpersand( tabWidget->tabLabel( tabWidget->currentPage() ) ).contains( tr( "Contents" ) ) )
1144 showContentsTopic();
1145 else
1146 showBookmarkTopic();
1147 } else if ( id > 0 ) {
1148 HelpNavigationContentsItem *i = (HelpNavigationContentsItem*)item;
1149 if ( id == 1 )
1150 help->browsers()->currentBrowser()->openLinkInNewWindow( i->link() );
1151 else
1152 help->browsers()->currentBrowser()->openLinkInNewPage( i->link() );
1153 }
1154}
Note: See TracBrowser for help on using the repository browser.