tools/assistant/compat/mainwindow.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/assistant/compat/mainwindow.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,885 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Assistant of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "tabbedbrowser.h"
+#include "helpdialog.h"
+#include "config.h"
+#include "fontsettingsdialog.h"
+
+#include <QDockWidget>
+#include <QDir>
+#include <QTimer>
+#include <QStatusBar>
+#include <QShortcut>
+#include <QMessageBox>
+#include <QPainter>
+#include <QEventLoop>
+#include <QtEvents>
+#include <QFontDatabase>
+#include <QWhatsThis>
+#include <QTextDocumentFragment>
+#include <QLibraryInfo>
+#include <QPrinter>
+#include <QPrintDialog>
+#include <QAbstractTextDocumentLayout>
+#include <QTextDocument>
+#include <QTextObject>
+#include <QFileDialog>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+QList<MainWindow*> MainWindow::windows;
+
+#if defined(Q_WS_WIN)
+extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
+#endif
+
+MainWindow::MainWindow()
+{
+    setUnifiedTitleAndToolBarOnMac(true);
+    ui.setupUi(this);
+
+#if defined(Q_WS_WIN)
+    // Workaround for QMimeSourceFactory failing in QFileInfo::isReadable() for
+    // certain user configs. See task: 34372
+    qt_ntfs_permission_lookup = 0;
+#endif
+    setupCompleted = false;
+
+    goActions = QList<QAction*>();
+    goActionDocFiles = new QMap<QAction*,QString>;
+
+    windows.append(this);
+    tabs = new TabbedBrowser(this);
+    connect(tabs, SIGNAL(tabCountChanged(int)), this, SLOT(updateTabActions(int)));
+    setCentralWidget(tabs);
+
+    Config *config = Config::configuration();
+
+    updateProfileSettings();
+
+    dw = new QDockWidget(this);
+    dw->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+    dw->setWindowTitle(tr("Sidebar"));
+    dw->setObjectName(QLatin1String("sidebar"));
+    helpDock = new HelpDialog(dw, this);
+    dw->setWidget(helpDock);
+
+    addDockWidget(Qt::LeftDockWidgetArea, dw);
+
+    // read geometry configuration
+    setupGoActions();
+
+    restoreGeometry(config->windowGeometry());
+    restoreState(config->mainWindowState());
+    if (config->sideBarHidden())
+        dw->hide();
+
+    tabs->setup();
+    QTimer::singleShot(0, this, SLOT(setup()));
+#if defined(Q_WS_MAC)
+    QMenu *windowMenu = new QMenu(tr("&Window"), this);
+    menuBar()->insertMenu(ui.helpMenu->menuAction(), windowMenu);
+    windowMenu->addAction(tr("Minimize"), this,
+        SLOT(showMinimized()), QKeySequence(tr("Ctrl+M")));
+    // Use the same forward and backward browser shortcuts as Safari and Internet Explorer do
+    // on the Mac. This means that if you have access to one of those cool Intellimice, the thing
+    // works just fine, since that's how Microsoft hacked it.
+    ui.actionGoPrevious->setShortcut(QKeySequence(Qt::CTRL|Qt::Key_Left));
+    ui.actionGoNext->setShortcut(QKeySequence(Qt::CTRL|Qt::Key_Right));
+
+    static const QLatin1String MacIconPath(":/trolltech/assistant/images/mac");
+    ui.actionGoNext->setIcon(QIcon(MacIconPath + QLatin1String("/next.png")));
+    ui.actionGoPrevious->setIcon(QIcon(MacIconPath + QLatin1String("/prev.png")));
+    ui.actionGoHome->setIcon(QIcon(MacIconPath + QLatin1String("/home.png")));
+    ui.actionEditCopy->setIcon(QIcon(MacIconPath + QLatin1String("/editcopy.png")));
+    ui.actionEditCopy->setIcon(QIcon(MacIconPath + QLatin1String("/editcopy.png")));
+    ui.actionEditFind->setIcon(QIcon(MacIconPath + QLatin1String("/find.png")));
+    ui.actionFilePrint->setIcon(QIcon(MacIconPath + QLatin1String("/print.png")));
+    ui.actionZoomOut->setIcon(QIcon(MacIconPath + QLatin1String("/zoomout.png")));
+    ui.actionZoomIn->setIcon(QIcon(MacIconPath + QLatin1String("/zoomin.png")));
+    ui.actionSyncToc->setIcon(QIcon(MacIconPath + QLatin1String("/synctoc.png")));
+    ui.actionHelpWhatsThis->setIcon(QIcon(MacIconPath + QLatin1String("/whatsthis.png")));
+#elif defined(Q_WS_X11)
+    ui.actionGoNext->setIcon(QIcon::fromTheme("go-next" , ui.actionGoNext->icon()));
+    ui.actionGoPrevious->setIcon(QIcon::fromTheme("go-previous" , ui.actionGoPrevious->icon()));
+    ui.actionGoHome->setIcon(QIcon::fromTheme("user-home" , ui.actionGoHome->icon()));
+    ui.actionEditCopy->setIcon(QIcon::fromTheme("edit-copy" , ui.actionEditCopy->icon()));
+    ui.actionEditFind->setIcon(QIcon::fromTheme("edit-find" , ui.actionEditFind->icon()));
+    ui.actionFilePrint->setIcon(QIcon::fromTheme("document-print" , ui.actionFilePrint->icon()));
+    ui.actionZoomOut->setIcon(QIcon::fromTheme("zoom-out" , ui.actionZoomOut->icon()));
+    ui.actionZoomIn->setIcon(QIcon::fromTheme("zoom-in" , ui.actionZoomIn->icon()));
+    ui.actionSyncToc->setIcon(QIcon::fromTheme("view-refresh" , ui.actionSyncToc->icon()));
+#endif
+}
+
+MainWindow::~MainWindow()
+{
+    windows.removeAll(this);
+    delete goActionDocFiles;
+}
+
+void MainWindow::setup()
+{
+    if(setupCompleted)
+        return;
+
+    qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
+    statusBar()->showMessage(tr("Initializing Qt Assistant..."));
+    setupCompleted = true;
+    helpDock->initialize();
+    connect(ui.actionGoPrevious, SIGNAL(triggered()), tabs, SLOT(backward()));
+    connect(ui.actionGoNext, SIGNAL(triggered()), tabs, SLOT(forward()));
+    connect(ui.actionEditCopy, SIGNAL(triggered()), tabs, SLOT(copy()));
+    connect(ui.actionFileExit, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
+    connect(ui.actionAddBookmark, SIGNAL(triggered()),
+             helpDock, SLOT(addBookmark()));
+    connect(helpDock, SIGNAL(showLink(QString)),
+             this, SLOT(showLink(QString)));
+    connect(helpDock, SIGNAL(showSearchLink(QString,QStringList)),
+             this, SLOT(showSearchLink(QString,QStringList)));
+
+    connect(ui.bookmarkMenu, SIGNAL(triggered(QAction*)),
+             this, SLOT(showBookmark(QAction*)));
+    connect(ui.actionZoomIn, SIGNAL(triggered()), tabs, SLOT(zoomIn()));
+    connect(ui.actionZoomOut, SIGNAL(triggered()), tabs, SLOT(zoomOut()));
+
+    connect(ui.actionOpenPage, SIGNAL(triggered()), tabs, SLOT(newTab()));
+    connect(ui.actionClosePage, SIGNAL(triggered()), tabs, SLOT(closeTab()));
+    connect(ui.actionNextPage, SIGNAL(triggered()), tabs, SLOT(nextTab()));
+    connect(ui.actionPrevPage, SIGNAL(triggered()), tabs, SLOT(previousTab()));
+
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
+    QShortcut *acc = new QShortcut(tr("SHIFT+CTRL+="), this);
+    connect(acc, SIGNAL(activated()), ui.actionZoomIn, SIGNAL(triggered()));
+#endif
+
+    connect(new QShortcut(tr("Ctrl+T"), this), SIGNAL(activated()), helpDock, SLOT(toggleContents()));
+    connect(new QShortcut(tr("Ctrl+I"), this), SIGNAL(activated()), helpDock, SLOT(toggleIndex()));
+    connect(new QShortcut(tr("Ctrl+B"), this), SIGNAL(activated()), helpDock, SLOT(toggleBookmarks()));
+    connect(new QShortcut(tr("Ctrl+S"), this), SIGNAL(activated()), helpDock, SLOT(toggleSearch()));
+    connect(new QShortcut(tr("Ctrl+]"), this), SIGNAL(activated()), tabs, SLOT(nextTab()));
+    connect(new QShortcut(tr("Ctrl+["), this), SIGNAL(activated()), tabs, SLOT(previousTab()));
+
+    Config *config = Config::configuration();
+
+    setupBookmarkMenu();
+
+    QAction *viewsAction = createPopupMenu()->menuAction();
+    viewsAction->setText(tr("Views"));
+    ui.viewMenu->addAction(viewsAction);
+
+    const int tabIndex = config->sideBarPage();
+    helpDock->tabWidget()->setCurrentIndex(tabIndex);
+    // The tab index is 0 by default, so we need to force an upate
+    // to poulate the contents in this case.
+    if (tabIndex == 0)
+        helpDock->currentTabChanged(tabIndex);
+
+    ui.actionEditFind->setShortcut(QKeySequence::Find);
+    ui.actionEditFindNext->setShortcut(QKeySequence::FindNext);
+    ui.actionEditFindPrev->setShortcut(QKeySequence::FindPrevious);
+
+    QObject::connect(ui.actionEditFind, SIGNAL(triggered()), tabs, SLOT(find()));
+    QObject::connect(ui.actionEditFindNext, SIGNAL(triggered()), tabs, SLOT(findNext()));
+    QObject::connect(ui.actionEditFindPrev, SIGNAL(triggered()), tabs, SLOT(findPrevious()));
+    connect(ui.actionEditFont_Settings, SIGNAL(triggered()), this, SLOT(showFontSettingsDialog()));
+
+	qApp->restoreOverrideCursor();
+    ui.actionGoPrevious->setEnabled(false);
+    ui.actionGoNext->setEnabled(false);
+    ui.actionEditCopy->setEnabled(false);
+
+    // set the current selected item in the treeview
+    helpDialog()->locateContents(tabs->currentBrowser()->source().toString());
+    connect(tabs, SIGNAL(browserUrlChanged(QString)), helpDock, SLOT(locateContents(QString)));
+}
+
+void MainWindow::browserTabChanged()
+{
+    HelpWindow *win = tabs->currentBrowser();
+    if (win) {
+        QTextCursor cursor(win->textCursor());
+        ui.actionEditCopy->setEnabled(cursor.hasSelection());
+        ui.actionGoPrevious->setEnabled(win->isBackwardAvailable());
+        ui.actionGoNext->setEnabled(win->isForwardAvailable());
+    }
+}
+
+void MainWindow::copyAvailable(bool yes)
+{
+    ui.actionEditCopy->setEnabled(yes);
+}
+
+void MainWindow::updateTabActions(int index)
+{
+    bool enabled = (index > 1) ? true : false;
+    ui.actionPrevPage->setEnabled(enabled);
+    ui.actionNextPage->setEnabled(enabled);
+    ui.actionClosePage->setEnabled(enabled);
+}
+
+void MainWindow::setupGoActions()
+{
+    Config *config = Config::configuration();
+    QStringList titles = config->docTitles();
+    QAction *action = 0;
+
+    static bool separatorInserted = false;
+
+    foreach (QAction *a, goActions) {
+        ui.goMenu->removeAction(a);
+        ui.goActionToolbar->removeAction(a);
+    }
+    qDeleteAll(goActions);
+    goActions.clear();
+    goActionDocFiles->clear();
+
+    int addCount = 0;
+
+    foreach (QString title, titles) {
+        QPixmap pix = config->docIcon(title);
+        if(!pix.isNull()) {
+            if(!separatorInserted) {
+                ui.goMenu->addSeparator();
+                separatorInserted = true;
+            }
+            action = new QAction(this);
+            action->setText(title);
+            action->setWhatsThis(tr("Displays the main page of a specific documentation set."));
+            action->setIcon(QIcon(pix));
+            ui.goMenu->addAction(action);
+            ui.goActionToolbar->addAction(action);
+            goActions.append(action);
+            goActionDocFiles->insert(action, config->indexPage(title));
+            connect(action, SIGNAL(triggered()),
+                     this, SLOT(showGoActionLink()));
+            ++addCount;
+        }
+    }
+    if(!addCount)
+        ui.goActionToolbar->hide();
+    else
+        ui.goActionToolbar->show();
+
+}
+
+bool MainWindow::insertActionSeparator()
+{
+    ui.goMenu->addSeparator();
+    ui.Toolbar->addSeparator();
+    return true;
+}
+
+void MainWindow::closeEvent(QCloseEvent *e)
+{
+    saveSettings();
+    e->accept();
+}
+
+void MainWindow::about()
+{
+    QMessageBox box(this);
+
+    box.setText(QString::fromLatin1("<center><img src=\":/trolltech/assistant/images/assistant-128.png\">"
+                                    "<h3>%1</h3>"
+                                    "<p>Version %2</p></center>"
+                                    "<p>Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).</p>")
+                   .arg(tr("Qt Assistant")).arg(QLatin1String(QT_VERSION_STR)));
+    box.setWindowTitle(tr("Qt Assistant"));
+    box.setIcon(QMessageBox::NoIcon);
+    box.exec();
+}
+
+void MainWindow::on_actionAboutApplication_triggered()
+{
+    QString url = Config::configuration()->aboutURL();
+    if (url == QLatin1String("about_qt")) {
+        QMessageBox::aboutQt(this, QLatin1String("Qt Assistant"));
+        return;
+    }
+    QString text;
+    if (url.startsWith(QLatin1String("file:")))
+        url = url.mid(5);
+    QFile file(url);
+    if(file.exists() && file.open(QFile::ReadOnly))
+        text = QString::fromUtf8(file.readAll());
+    if(text.isNull())
+        text = tr("Failed to open about application contents in file: '%1'").arg(url);
+
+    QFileInfo fi(file);
+    QString path = QDir::cleanPath(fi.absolutePath());
+    if (!QDir::searchPaths(QLatin1String("aboutImages")).contains(path))
+        QDir::addSearchPath(QLatin1String("aboutImages"), path);
+
+    QMessageBox box(this);
+    box.setText(text);
+    box.setWindowTitle(Config::configuration()->aboutApplicationMenuText());
+    box.setIcon(QMessageBox::NoIcon);
+    box.exec();
+}
+
+void MainWindow::on_actionAboutAssistant_triggered()
+{
+    about();
+}
+
+void MainWindow::on_actionGoHome_triggered()
+{
+    QString home = MainWindow::urlifyFileName(Config::configuration()->homePage());
+    showLink(home);
+}
+
+QString MainWindow::urlifyFileName(const QString &fileName)
+{
+    QString name = fileName;
+    QUrl url(name);
+
+#if defined(Q_OS_WIN32)
+    if (!url.isValid() || url.scheme().isEmpty() || url.scheme().toLower() != QLatin1String("file:")) {
+        int i = name.indexOf(QLatin1Char('#'));
+        QString anchor = name.mid(i);
+        name = name.toLower();
+        if (i > -1)
+            name.replace(i, anchor.length(), anchor);
+        name.replace(QLatin1Char('\\'), QLatin1Char('/'));
+        foreach (QFileInfo drive, QDir::drives()) {
+            if (name.startsWith(drive.absolutePath().toLower())) {
+                name = QLatin1String("file:") + name;
+                break;
+            }
+        }
+    }
+#else
+    if (!url.isValid() || url.scheme().isEmpty())
+        name.prepend(QLatin1String("file:"));
+#endif
+    return name;
+}
+
+#ifndef QT_NO_PRINTER
+class PrintThread : public QThread
+{
+    QPrinter _printer;
+    QTextDocument *_document;
+
+public:
+    PrintThread(QObject *parent)
+        : QThread(parent), _printer(QPrinter::HighResolution), _document(0)
+    {
+    }
+    ~PrintThread()
+    {
+        wait();
+    }
+
+    QPrinter *printer()
+    {
+        return &_printer;
+    }
+
+    void start(QTextDocument *document)
+    {
+        _document = document->clone();
+        _document->moveToThread(this);
+        QThread::start();
+    }
+
+protected:
+    void run()
+    {
+        _document->print(printer());
+        delete _document;
+        _document = 0;
+    }
+};
+#endif //QT_NO_PRINTER
+
+void MainWindow::on_actionFilePrint_triggered()
+{
+#ifndef QT_NO_PRINTER
+    if (!QFontDatabase::supportsThreadedFontRendering()) {
+        QPrinter printer(QPrinter::HighResolution);
+
+        QPrintDialog dlg(&printer, this);
+        if (dlg.exec() == QDialog::Accepted) {
+            qApp->setOverrideCursor(Qt::WaitCursor);
+            tabs->currentBrowser()->document()->print(&printer);
+            qApp->restoreOverrideCursor();
+        }
+        return;
+    }
+
+    PrintThread *thread = new PrintThread(this);
+
+    QPrintDialog dlg(thread->printer(), this);
+    if (dlg.exec() == QDialog::Accepted) {
+        connect(thread, SIGNAL(finished()), SLOT(printingFinished()));
+        connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+
+        qApp->setOverrideCursor(Qt::BusyCursor);
+        thread->start(tabs->currentBrowser()->document());
+    } else {
+        delete thread;
+    }
+#else
+    Q_ASSERT("No printing support");
+#endif
+}
+
+void MainWindow::printingFinished()
+{
+    qApp->restoreOverrideCursor();
+}
+
+void MainWindow::updateBookmarkMenu()
+{
+    for(QList<MainWindow*>::Iterator it = windows.begin(); it != windows.end(); ++it)
+        (*it)->setupBookmarkMenu();
+}
+
+void MainWindow::setupBookmarkMenu()
+{
+    ui.bookmarkMenu->clear();
+    bookmarks.clear();
+    ui.bookmarkMenu->addAction(ui.actionAddBookmark);
+
+    QFile f(QDir::homePath() + QLatin1String("/.assistant/bookmarks.") +
+        Config::configuration()->profileName());
+    if (!f.open(QFile::ReadOnly))
+        return;
+    QTextStream ts(&f);
+    ui.bookmarkMenu->addSeparator();
+    while (!ts.atEnd()) {
+        QString title = ts.readLine();
+        QString link = ts.readLine();
+        bookmarks.insert(ui.bookmarkMenu->addAction(title), link);
+    }
+}
+
+void MainWindow::showBookmark(QAction *action)
+{
+    if (bookmarks.contains(action))
+        showLink(bookmarks.value(action));
+}
+
+void MainWindow::showLinkFromClient(const QString &link)
+{
+    setWindowState(windowState() & ~Qt::WindowMinimized);
+    raise();
+    activateWindow();
+    QString l = MainWindow::urlifyFileName(link);
+    showLink(l);
+    if (isMinimized())
+        showNormal();
+}
+
+void MainWindow::showLink(const QString &link)
+{
+    if(link.isEmpty())
+        qWarning("The link is empty!");
+
+    // don't fill the history with the same url more then once
+    if (link == tabs->currentBrowser()->source().toString())
+        return;
+
+    QUrl url(link);
+    QFileInfo fi(url.toLocalFile());
+    tabs->setSource(url.toString());
+    tabs->currentBrowser()->setFocus();
+}
+
+void MainWindow::showLinks(const QStringList &links)
+{
+    if (links.size() == 0) {
+        qWarning("MainWindow::showLinks() - Empty link");
+        return;
+    }
+
+    if (links.size() == 1) {
+        showLink(MainWindow::urlifyFileName(links.first()));
+        return;
+    }
+
+    QStringList::ConstIterator it = links.begin();
+    // Initial showing, The tab is empty so update that without creating it first
+    if (!tabs->currentBrowser()->source().isValid()) {
+        QPair<HelpWindow*, QString> browser;
+        browser.first = tabs->currentBrowser();
+        browser.second = links.first();
+        pendingBrowsers.append(browser);
+        tabs->setTitle(tabs->currentBrowser(), tr("..."));
+    }
+    ++it;
+
+    while(it != links.end()) {
+        QPair<HelpWindow*, QString> browser;
+        browser.first = tabs->newBackgroundTab();
+        browser.second = *it;
+        pendingBrowsers.append(browser);
+        ++it;
+    }
+
+    startTimer(50);
+    return;
+}
+
+void MainWindow::removePendingBrowser(HelpWindow *win)
+{
+    if (!pendingBrowsers.count())
+        return;
+
+    QMutableListIterator<QPair<HelpWindow*, QString> > it(pendingBrowsers);
+    while (it.hasNext()) {
+        QPair<HelpWindow*, QString> browser = it.next();
+        if (browser.first == win) {
+            it.remove();
+            break;
+        }
+    }
+}
+
+void MainWindow::timerEvent(QTimerEvent *e)
+{
+    QPair<HelpWindow*, QString> browser = pendingBrowsers.first();
+    pendingBrowsers.pop_front();
+
+    if (pendingBrowsers.size() == 0)
+        killTimer(e->timerId());
+
+    browser.first->setSource(MainWindow::urlifyFileName(browser.second));
+}
+
+void MainWindow::showQtHelp()
+{
+    showLink(QLibraryInfo::location(QLibraryInfo::DocumentationPath) +
+             QLatin1String("/html/index.html"));
+}
+
+MainWindow* MainWindow::newWindow()
+{
+    saveSettings();
+    MainWindow *mw = new MainWindow;
+    mw->move(geometry().topLeft());
+    if (isMaximized())
+        mw->showMaximized();
+    else
+        mw->show();
+    mw->on_actionGoHome_triggered();
+    return mw;
+}
+
+void MainWindow::saveSettings()
+{
+    Config *config = Config::configuration();
+
+    config->setSideBarPage(helpDock->tabWidget()->currentIndex());
+    config->setWindowGeometry(saveGeometry());
+    config->setMainWindowState(saveState());
+
+    // Create list of the tab urls
+    QStringList lst;
+    QList<HelpWindow*> browsers = tabs->browsers();
+    foreach (HelpWindow *browser, browsers)
+        lst << browser->source().toString();
+    config->setSource(lst);
+    config->save();
+}
+
+TabbedBrowser* MainWindow::browsers() const
+{
+    return tabs;
+}
+
+void MainWindow::showSearchLink(const QString &link, const QStringList &terms)
+{
+    HelpWindow * hw = tabs->currentBrowser();
+    hw->blockScrolling(true);
+    hw->setCursor(Qt::WaitCursor);
+    if (hw->source() == link)
+        hw->reload();
+    else
+        showLink(link);
+    hw->setCursor(Qt::ArrowCursor);
+
+    hw->viewport()->setUpdatesEnabled(false);
+
+    QTextCharFormat marker;
+    marker.setForeground(Qt::red);
+
+    QTextCursor firstHit;
+
+    QTextCursor c = hw->textCursor();
+    c.beginEditBlock();
+    foreach (QString term, terms) {
+        c.movePosition(QTextCursor::Start);
+        hw->setTextCursor(c);
+
+        bool found = hw->find(term, QTextDocument::FindWholeWords);
+        while (found) {
+            QTextCursor hit = hw->textCursor();
+            if (firstHit.isNull() || hit.position() < firstHit.position())
+                firstHit = hit;
+
+            hit.mergeCharFormat(marker);
+            found = hw->find(term, QTextDocument::FindWholeWords);
+        }
+    }
+
+    if (firstHit.isNull()) {
+        firstHit = hw->textCursor();
+        firstHit.movePosition(QTextCursor::Start);
+    }
+    firstHit.clearSelection();
+    c.endEditBlock();
+    hw->setTextCursor(firstHit);
+
+    hw->blockScrolling(false);
+    hw->viewport()->setUpdatesEnabled(true);
+}
+
+
+void MainWindow::showGoActionLink()
+{
+    const QObject *origin = sender();
+    if(!origin ||
+        QString::fromLatin1(origin->metaObject()->className()) != QString::fromLatin1("QAction"))
+        return;
+
+    QAction *action = (QAction*) origin;
+    QString docfile = *(goActionDocFiles->find(action));
+    showLink(MainWindow::urlifyFileName(docfile));
+}
+
+void MainWindow::on_actionHelpAssistant_triggered()
+{
+    showLink(Config::configuration()->assistantDocPath() + QLatin1String("/assistant-manual.html"));
+}
+
+HelpDialog* MainWindow::helpDialog() const
+{
+    return helpDock;
+}
+
+void MainWindow::backwardAvailable(bool enable)
+{
+    ui.actionGoPrevious->setEnabled(enable);
+}
+
+void MainWindow::forwardAvailable(bool enable)
+{
+    ui.actionGoNext->setEnabled(enable);
+}
+
+void MainWindow::updateProfileSettings()
+{
+    Config *config = Config::configuration();
+#ifndef Q_WS_MAC
+    setWindowIcon(config->applicationIcon());
+#endif
+    ui.helpMenu->clear();
+    //ui.helpMenu->addAction(ui.actionHelpAssistant);
+    //ui.helpMenu->addSeparator();
+    ui.helpMenu->addAction(ui.actionAboutAssistant);
+    if (!config->aboutApplicationMenuText().isEmpty())
+        ui.helpMenu->addAction(ui.actionAboutApplication);
+    ui.helpMenu->addSeparator();
+    ui.helpMenu->addAction(ui.actionHelpWhatsThis);
+
+    ui.actionAboutApplication->setText(config->aboutApplicationMenuText());
+
+    if(!config->title().isNull())
+        setWindowTitle(config->title());
+}
+
+void MainWindow::setupPopupMenu(QMenu *m)
+{
+    m->addAction(ui.actionNewWindow);
+    m->addAction(ui.actionOpenPage);
+    m->addAction(ui.actionClosePage);
+    m->addSeparator();
+    m->addAction(ui.actionSaveAs);
+    m->addSeparator();
+    m->addAction(ui.actionGoPrevious);
+    m->addAction(ui.actionGoNext);
+    m->addAction(ui.actionGoHome);
+    m->addSeparator();
+    m->addAction(ui.actionZoomIn);
+    m->addAction(ui.actionZoomOut);
+    m->addSeparator();
+    m->addAction(ui.actionEditCopy);
+    m->addAction(ui.actionEditFind);
+}
+
+void MainWindow::on_actionSyncToc_triggered()
+{
+    HelpWindow *w = tabs->currentBrowser();
+    if(w) {
+        qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
+        QString  link = w->source().toString();
+        helpDock->locateContents(link);
+        helpDock->tabWidget()->setCurrentIndex(0);
+     	qApp->restoreOverrideCursor();
+    }
+}
+
+void MainWindow::on_actionNewWindow_triggered()
+{
+    newWindow()->show();
+}
+
+void MainWindow::on_actionClose_triggered()
+{
+    close();
+}
+
+void MainWindow::on_actionHelpWhatsThis_triggered()
+{
+    QWhatsThis::enterWhatsThisMode();
+}
+
+void MainWindow::on_actionSaveAs_triggered()
+{
+    QString fileName;
+    QUrl url = tabs->currentBrowser()->source();
+    if (url.isValid()) {
+        QFileInfo fi(url.toLocalFile());
+        fileName = fi.fileName();
+    }
+    fileName = QFileDialog::getSaveFileName(this, tr("Save Page"), fileName);
+    if (fileName.isEmpty())
+        return;
+
+    QFile file(fileName);
+    if (!file.open(QIODevice::WriteOnly)) {
+        QMessageBox::critical(this, tr("Save Page"), tr("Cannot open file for writing!"));
+        return;
+    }
+
+    QFileInfo fi(fileName);
+    QString fn = fi.fileName();
+    int i = fn.lastIndexOf(QLatin1Char('.'));
+    if (i > -1)
+        fn = fn.left(i);
+    QString relativeDestPath = fn + QLatin1String("_images");
+    QDir destDir(fi.absolutePath() + QDir::separator() + relativeDestPath);
+    bool imgDirAvailable = destDir.exists();
+    if (!imgDirAvailable)
+        imgDirAvailable = destDir.mkdir(destDir.absolutePath());
+
+    // save images
+    QTextDocument *doc = tabs->currentBrowser()->document()->clone();
+    if (url.isValid() && imgDirAvailable) {
+        QTextBlock::iterator it;
+        for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
+            for (it = block.begin(); !(it.atEnd()); ++it) {
+                QTextFragment fragment = it.fragment();
+                if (fragment.isValid()) {
+                    QTextImageFormat fm = fragment.charFormat().toImageFormat();
+                    if (fm.isValid() && !fm.name().isEmpty()) {
+                        QUrl imagePath = tabs->currentBrowser()->source().resolved(fm.name());
+                        if (!imagePath.isValid())
+                            continue;
+                        QString from = imagePath.toLocalFile();
+                        QString destName = fm.name();
+                        int j = destName.lastIndexOf(QLatin1Char('/'));
+                        if (j > -1)
+                            destName = destName.mid(j+1);
+                        QFileInfo info(from);
+                        if (info.exists()) {
+                            if (!QFile::copy(from, destDir.absolutePath()
+                                + QDir::separator() + destName))
+                                continue;
+                            fm.setName(QLatin1String("./") + relativeDestPath + QLatin1String("/") + destName);
+                            QTextCursor cursor(doc);
+                            cursor.setPosition(fragment.position());
+                            cursor.setPosition(fragment.position() + fragment.length(),
+                                QTextCursor::KeepAnchor);
+                            cursor.setCharFormat(fm);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    QString src = doc->toHtml(QByteArray("utf-8"));
+    QTextStream s(&file);
+    s.setCodec("utf-8");
+    s << src;
+    s.flush();
+    file.close();
+}
+
+void MainWindow::showFontSettingsDialog()
+{
+    Config *config = Config::configuration();
+    FontSettings settings = config->fontSettings();
+
+    { // It is important that the dialog be deleted before UI mode changes.
+        FontSettingsDialog dialog;
+        if (!dialog.showDialog(&settings))
+            return;
+    }
+
+    config->setFontPointSize(settings.browserFont.pointSizeF());
+    config->setFontSettings(settings);
+
+    updateApplicationFontSettings(settings);
+}
+
+void MainWindow::updateApplicationFontSettings(FontSettings &settings)
+{
+    QFont font = settings.windowFont;
+    if (this->font() != font)
+        qApp->setFont(font, "QWidget");
+
+    font = settings.browserFont;
+    QList<HelpWindow*> browsers = tabs->browsers();
+    foreach (HelpWindow *browser, browsers) {
+        if (browser->font() != font)
+            browser->setFont(font);
+    }
+}
+
+QT_END_NAMESPACE