/*! @file

    @id $Id: browser.hxx 15 2010-06-03 14:17:27Z marc $
*/
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

#ifndef BROWSER_HXX
#define BROWSER_HXX

#include <smartcardauth.hxx>
#include <logincertificate.hxx>
#include <errorlog.hxx>
#include <downloadmanager.hxx>
#include <authentication.hxx>
#include <settings.hxx>
#include <editbookmarks.hxx>
#include <temporaryfile.hxx>
#include <proxyface/proxy.hxx>
#include <sslclientnetworkmanager.hxx>

#include <ui_browser.h>

#include <qbrowserlib/buttonlineedit.hxx>
#include <qbrowserlib/filestorage.hxx>
#include <qbrowserlib/swisswebview.hxx>

#include <QtGui/QMainWindow>
#include <QtGui/QProgressBar>
#include <QtGui/QComboBox>
#include <QtGui/QSlider>
#include <QtGui/QMessageBox>
#include <QtGui/QPrinter>
#include <QtGui/QPrintDialog>
#include <QtGui/QPrintPreviewDialog>
#include <QtWebKit/QWebFrame>
#include <QtWebKit/QWebHistory>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QSslError>
#include <QtNetwork/QNetworkProxy>
#include <QtCore/QProcess>
#include <QtCore/QDir>
#include <QtGui/QFileDialog>

#include <QtGui/QDesktopServices>

#include <stdexcept>
#include <cassert>
#include <memory>

#include <QtCore/QDebug>
#ifndef LOG
#define LOG qDebug()<<__PRETTY_FUNCTION__
#endif

extern QString TMP;

//! @addtogroup browser
//! @{

class Browser: public QMainWindow, protected Ui::Browser {

    Q_OBJECT;

  public:

    Browser(const QString& actlib, const QStringList& urls = QStringList(),
            QSettings* settings=0,
            Settings::MimeTypes mimeTypes = Settings::MimeTypes(),
            bool kiosk = false, bool login = true, bool quirks=true,
            QString bookmarkfile=QString()):
        _url(0), _find(new ButtonLineEdit),
        _kiosk(kiosk),
        _downloadManager(new DownloadManager),
        _settings(mimeTypes, this, settings, !kiosk),
        _errorLog(this), _logincertificate(this),
        _proxy("http://swisssign.com", this),
        _showErrorLog(0),
        _startUrl(urls.size()),
        _quirks(quirks), _search(new ButtonLineEdit),
        _searchEngines(new QComboBox),
        _bookmarkfile(bookmarkfile) {
      LOG<<urls;
      _home = "about:blank";
      if (urls.size()) _home = urls.at(0);
      setupUi(this);
      actionStop->setVisible(false);
      actionReload->setVisible(false);
      show();
      try {
        _networkManager.scAuth(new SmartCardAuth(actlib, this, login));
      } catch (const std::exception& x) {
        LOG<<"No SmartCard Support:"<<x.what();
      }
      statusBar()->addPermanentWidget(_find);
      _find->add(actionUnFind);
      _find->add(actionClearFind);
      _find->hide();
      statusBar()->addPermanentWidget(_progress = new QProgressBar());
      QSizePolicy sp(_progress->sizePolicy());
      sp.setHorizontalPolicy(QSizePolicy::Minimum);
      _progress->setSizePolicy(sp);
      QPushButton* btn(0);
      statusBar()->addPermanentWidget
        (btn=new QPushButton(QIcon(":/icons/16x16/butt16_09.png"), ""));
      assert(connect(btn, SIGNAL(clicked(bool)), SLOT(smaller())));
      statusBar()->addPermanentWidget(_zoom = new QSlider(Qt::Horizontal));
      statusBar()->addPermanentWidget
        (btn=new QPushButton(QIcon(":/icons/16x16/butt16_10.png"), ""));
      assert(connect(btn, SIGNAL(clicked(bool)), SLOT(larger())));
      sp = _zoom->sizePolicy();
      sp.setHorizontalPolicy(QSizePolicy::Minimum);
      _zoom->setSizePolicy(sp);
      _zoom->setMinimum(10);
      _zoom->setMaximum(190);
      _zoom->setValue(100);
      _zoom->setSingleStep(1);
      _zoom->setPageStep(10);
      _zoom->setTickPosition(QSlider::TicksAbove);
      assert(connect(_zoom, SIGNAL(valueChanged(int)), SLOT(zoom(int))));
      if (_kiosk||!_quirks) {
        _toolbar->addAction(actionReload);
        _toolbar->addAction(actionStop);
      }
      if (!_kiosk) {
        QComboBox* combo(new QComboBox(_toolbar));
        _url = combo;
        QFrame* urlFrame(new QFrame);
        QHBoxLayout* layout(new QHBoxLayout);
        urlFrame->setLayout(layout);
        _toolbar->addWidget(urlFrame);
        layout->addWidget(_url);
        combo->setInsertPolicy(QComboBox::NoInsert);
        combo->setLineEdit(new ButtonLineEdit(combo));
        combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
        sp=combo->sizePolicy();
        sp.setHorizontalPolicy(QSizePolicy::Expanding);
        combo->setSizePolicy(sp);
        combo->setEditable(!_kiosk);
        assert(connect(combo, SIGNAL(currentIndexChanged(const QString&)),
                       SLOT(load(QString))));
        assert(connect(combo->lineEdit(), SIGNAL(returnPressed()),
                       SLOT(load())));
        assert(connect(combo->lineEdit(), SIGNAL(textChanged(QString)),
                       SLOT(goodUrl())));
        if (_quirks) {
          dynamic_cast<ButtonLineEdit*>(combo->lineEdit())->add
            (actionStop);
          dynamic_cast<ButtonLineEdit*>(combo->lineEdit())->add
            (actionReload);
        }
        if (!_quirks) {
          dynamic_cast<ButtonLineEdit*>(combo->lineEdit())->add
            (actionAddBookmark);
          dynamic_cast<ButtonLineEdit*>(combo->lineEdit())->add
            (actionClearLocation);
        }
        _editbookmarks =
          std::auto_ptr<EditBookmarks>(new EditBookmarks(combo, this));
        assert(connect(_editbookmarks.get(), SIGNAL(startEdit(bool)),
                       actionAddBookmark, SLOT(setDisabled(bool))));
        assert(connect(_editbookmarks.get(), SIGNAL(endEdit(bool)),
                       actionAddBookmark, SLOT(setEnabled(bool))));
        assert(connect(_editbookmarks.get(), SIGNAL(accepted()),
                       SLOT(saveBookmarks())));
      } else {
        QLineEdit* label(new QLineEdit(_toolbar));
        _url = label;
        label->setReadOnly(true);
        label->setEnabled(false);
        _toolbar->addWidget(_url);
      }
      if (!_kiosk) {
        QFrame* searchFrame(new QFrame);
        QHBoxLayout* layout(new QHBoxLayout);
        layout->addWidget(_searchEngines);
        layout->addWidget(_search);
        searchFrame->setLayout(layout);
        _toolbar->addWidget(searchFrame);
        _search->add(actionSearch);
      }
      if (!_quirks) _search->add(actionClearSearch);
      sp=_search->sizePolicy();
      sp.setHorizontalPolicy(QSizePolicy::Minimum);
      _search->setSizePolicy(sp);
      assert(connect(_search, SIGNAL(returnPressed()),
                     SLOT(on_actionSearch_triggered())));
      if (_quirks) {
        _toolbar->setMovable(false);
        _toolbar->setFloatable(false);
        if (!_kiosk) _toolbar->addAction(actionAddBookmark);
        _toolbar->addAction(actionHome);
      }
      assert(connect(&_errorLog, SIGNAL(reset()), SLOT(errorReset())));
      assert(connect(&_networkManager, SIGNAL(finished(QNetworkReply*)),
                     SLOT(finished(QNetworkReply*))));
      assert(connect(&_networkManager, SIGNAL(created(QNetworkReply*)),
                     _downloadManager.data(), SLOT(add(QNetworkReply*))));
      assert(connect(_downloadManager.data(), SIGNAL(progress(qint64, qint64)),
                     SLOT(progress(qint64, qint64))));
      assert(connect(_downloadManager.data(), SIGNAL(started()),
                     SLOT(started())));
      assert(connect(_downloadManager.data(), SIGNAL(finished()),
                     SLOT(finished())));
      assert(connect(_downloadManager.data(), SIGNAL(error(QString)),
                     SLOT(downloadError(QString))));
      assert(connect(_downloadManager.data(), SIGNAL(metaDataChanged(QNetworkReply*)),
                     SLOT(metaDataChanged(QNetworkReply*))));
      assert(connect(&_settings, SIGNAL(newSettings()), SLOT(newSettings())));
      newSettings();
      on_actionNewTab_triggered();

      if (!settings || _kiosk) {
        actionSettings->setVisible(false);
        actionSaveWindowState->setVisible(false);
        actionLoadWindowState->setVisible(false);
      }
      if (_kiosk) {
        actionNew->setEnabled(false);
        actionNew->setVisible(false);
        actionNewTab->setEnabled(false);
        actionNewTab->setVisible(false);
        actionCloseTab->setVisible(false);
        actionEditBookmarks->setVisible(false);
        delete _bookmarks; _bookmarks=0;
      }
      if (!_kiosk &&
          ((!_startUrl && _settings.flag("SaveWindowState") && _settings()) ||
           _bookmarkfile.readable()))
        loadWin();
      if (urls.size()) load(urls.at(0));
      for (int i(1); i<urls.size(); ++i) load(urls.at(i), newTab());
      if (!QSslSocket::supportsSsl())
        QMessageBox::critical(this, tr("SSL Not Supported"),
                              tr("SSL is not supported on your system"));
      // temporary caching of favicons
      QWebSettings::setIconDatabasePath(TMP);
    }

    ~Browser() {
      LOG;
      QWebSettings::clearIconDatabase();
      for (DownloadProcesses::iterator it(_downloadProcesses.begin());
           it!=_downloadProcesses.end(); ++it) {
        LOG<<"cleanup:"<<it->second->fileName();
        it->second->setAutoRemove(_settings.flag("CloseApps"));
        delete it->second;
        it->second = 0;
        if (_settings.flag("CloseApps")) {
          LOG<<"terminate process";
          it->first->terminate();
          delete it->first;
        }
      }
    }

    //! Whitelisting
    bool check(QUrl page) {
      return true;
    }

    //! Create new empty tab.
    SwissWebView* newTab() {
      SwissWebView* browser(new SwissWebView);
      newTab(browser);
      return browser;
    }

    
  protected:
    
    void closeEvent(QCloseEvent *event) {
      LOG;
      if (!_kiosk &&
          ((!_startUrl && _settings.flag("SaveWindowState") && _settings()) ||
           _bookmarkfile.writeable()))
        saveWin();
      QMainWindow::closeEvent(event);
      QApplication::exit(0);
    }


  public Q_SLOTS:

    void on_actionEditBookmarks_triggered() {
      _editbookmarks->show();
    }


  private Q_SLOTS:
      
    void load() {
      LOG;
      if (qobject_cast<QComboBox*>(_url))
        load(qobject_cast<QComboBox*>(_url)->currentText());
      else
        load(qobject_cast<QLineEdit*>(_url)->text());
    }
    
    void load(QString page) {
      _settings.replaceSearchEngine(page);
      if (QUrl(page).scheme()=="") page = "http://"+page;
      if (qobject_cast<QComboBox*>(_url))
        qobject_cast<QComboBox*>(_url)->lineEdit()->setText(page);
      else
        qobject_cast<QLineEdit*>(_url)->setText(page);
      load(QUrl(page));
    }

    void load(QUrl page, QWebView* view=0) {
      LOG<<page.toString();
      statusBar()->showMessage(tr("Checking: %1").arg(page.toString()));
      if (!check(page)) {
        LOG<<"########## BLACK LISTED IGNORED ##########";
        statusBar()->showMessage(tr("Forbidden: %1").arg(page.toString()));
        QMessageBox::warning(this, tr("Access Denied"),
                             tr("<p>Access denied due to security"
                                " considerations.</p><p>You are not"
                                " allowed to connect to %1.")
                             .arg(page.toString()));
        return;
      }
      statusBar()->showMessage(tr("Reading: %1").arg(page.toString()));
      if (!page.isValid()) {
        statusBar()->showMessage(tr("Illegal URL: %1").arg(page.errorString()));
        return;
      }
      if (!view) view=qobject_cast<QWebView*>(_tabs->currentWidget());
      view->load(page);
    }

    //! Slot that receives and initializes a new SwissWebView
    void newTab(SwissWebView* browser) {
      browser->page()->setNetworkAccessManager(&_networkManager);
      _url->setFocus();
      // SwissWebView
      assert(connect(browser, SIGNAL(newView(SwissWebView*)),
                     SLOT(newTab(SwissWebView*))));
      // QWebView
      assert(connect(browser, SIGNAL(urlChanged(const QUrl&)),
                     SLOT(urlChanged(const QUrl&))));
      assert(connect(browser, SIGNAL(linkClicked(const QUrl&)),
                     SLOT(linkClicked(const QUrl&))));
      assert(connect(browser, SIGNAL(iconChanged()),
                     SLOT(iconChanged())));
      assert(connect(browser, SIGNAL(titleChanged(const QString&)),
                     SLOT(titleChanged(const QString&))));
      assert(connect(browser, SIGNAL(statusBarMessage(const QString&)),
                     SLOT(statusBarMessage(const QString&))));
      assert(connect(browser, SIGNAL(loadProgress(int)),
                     SLOT(loadProgress(int))));
      assert(connect(browser, SIGNAL(loadStarted()),
                     SLOT(loadStarted())));
      assert(connect(browser, SIGNAL(loadFinished(bool)),
                     SLOT(loadFinished(bool))));
      // QWebPage WebAction
      assert(connect(browser->pageAction(QWebPage::OpenLink),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredOpenLink(bool))));
      assert(connect(browser->pageAction(QWebPage::OpenLinkInNewWindow),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredOpenLinkInNewWindow(bool))));
      assert(connect(browser->pageAction(QWebPage::OpenFrameInNewWindow),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredOpenFrameInNewWindow(bool))));
      assert(connect(browser->pageAction(QWebPage::DownloadLinkToDisk),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredDownloadLinkToDisk(bool))));
      assert(connect(browser->pageAction(QWebPage::CopyLinkToClipboard),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredCopyLinkToClipboard(bool))));
      assert(connect(browser->pageAction(QWebPage::OpenImageInNewWindow),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredOpenImageInNewWindow(bool))));
      assert(connect(browser->pageAction(QWebPage::DownloadImageToDisk),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredDownloadImageToDisk(bool))));
      assert(connect(browser->pageAction(QWebPage::CopyImageToClipboard),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredCopyImageToClipboard(bool))));
      assert(connect(browser->pageAction(QWebPage::Back),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredBack(bool))));
      assert(connect(browser->pageAction(QWebPage::Forward),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredForward(bool))));
      assert(connect(browser->pageAction(QWebPage::Stop),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredStop(bool))));
      assert(connect(browser->pageAction(QWebPage::Reload),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredReload(bool))));
      assert(connect(browser->pageAction(QWebPage::Cut),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredCut(bool))));
      assert(connect(browser->pageAction(QWebPage::Copy),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredCopy(bool))));
      assert(connect(browser->pageAction(QWebPage::Paste),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredPaste(bool))));
      assert(connect(browser->pageAction(QWebPage::Undo),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredUndo(bool))));
      assert(connect(browser->pageAction(QWebPage::Redo),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredRedo(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToNextChar),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToNextChar(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToPreviousChar),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToPreviousChar(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToNextWord),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToNextWord(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToPreviousWord),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToPreviousWord(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToNextLine),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToNextLine(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToPreviousLine),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToPreviousLine(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToStartOfLine),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToStartOfLine(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToEndOfLine),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToEndOfLine(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToStartOfBlock),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToStartOfBlock(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToEndOfBlock),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToEndOfBlock(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToStartOfDocument),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToStartOfDocument(bool))));
      assert(connect(browser->pageAction(QWebPage::MoveToEndOfDocument),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredMoveToEndOfDocument(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectNextChar),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectNextChar(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectPreviousChar),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectPreviousChar(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectNextWord),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectNextWord(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectPreviousWord),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectPreviousWord(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectNextLine),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectNextLine(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectPreviousLine),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectPreviousLine(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectStartOfLine),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectStartOfLine(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectEndOfLine),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectEndOfLine(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectStartOfBlock),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectStartOfBlock(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectEndOfBlock),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectEndOfBlock(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectStartOfDocument),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectStartOfDocument(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectEndOfDocument),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectEndOfDocument(bool))));
      assert(connect(browser->pageAction(QWebPage::DeleteStartOfWord),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredDeleteStartOfWord(bool))));
      assert(connect(browser->pageAction(QWebPage::DeleteEndOfWord),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredDeleteEndOfWord(bool))));
      assert(connect(browser->pageAction(QWebPage::SetTextDirectionDefault),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSetTextDirectionDefault(bool))));
      assert(connect(browser->pageAction(QWebPage::SetTextDirectionLeftToRight),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSetTextDirectionLeftToRight(bool))));
      assert(connect(browser->pageAction(QWebPage::SetTextDirectionRightToLeft),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSetTextDirectionRightToLeft(bool))));
      assert(connect(browser->pageAction(QWebPage::ToggleBold),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredToggleBold(bool))));
      assert(connect(browser->pageAction(QWebPage::ToggleItalic),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredToggleItalic(bool))));
      assert(connect(browser->pageAction(QWebPage::ToggleUnderline),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredToggleUnderline(bool))));
      assert(connect(browser->pageAction(QWebPage::InspectElement),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredInspectElement(bool))));
      assert(connect(browser->pageAction(QWebPage::InsertParagraphSeparator),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredInsertParagraphSeparator(bool))));
      assert(connect(browser->pageAction(QWebPage::InsertLineSeparator),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredInsertLineSeparator(bool))));
      assert(connect(browser->pageAction(QWebPage::SelectAll),
                     SIGNAL(triggered(bool)),
                     SLOT(triggeredSelectAll(bool))));
      // QWebPage
      assert(connect(browser->page(), SIGNAL(contentsChanged()),
                     SLOT(contentsChanged())));
      assert(connect(browser->page(),
                     SIGNAL(databaseQuotaExceeded(QWebFrame*, QString)),
                     SLOT(databaseQuotaExceeded(QWebFrame*, QString))));
      assert(connect(browser->page(),
                     SIGNAL(downloadRequested(const QNetworkRequest&)),
                     SLOT(downloadRequested(const QNetworkRequest&))));
      assert(connect(browser->page(), SIGNAL(frameCreated(QWebFrame*)),
                     SLOT(frameCreated(QWebFrame*))));
      assert(connect(browser->page(),
                     SIGNAL(geometryChangeRequested(const QRect&)),
                     SLOT(geometryChangeRequested(const QRect&))));
//       assert(connect(browser->page(), SIGNAL(linkClicked(const QUrl&)),
//                      SLOT(linkClicked(const QUrl&))));
      assert(connect(browser->page(),
                     SIGNAL(linkHovered(const QString&, const QString&,
                                        const QString&)),
                     SLOT(linkHovered(const QString&, const QString&,
                                      const QString&))));
//       assert(connect(browser->page(), SIGNAL(loadFinished(bool)),
//                      SLOT(loadFinished(bool))));
//       assert(connect(browser->page(), SIGNAL(loadProgress(int)),
//                      SLOT(loadProgress(int))));
//       assert(connect(browser->page(), SIGNAL(loadStarted()),
//                      SLOT(loadStarted())));
      assert(connect(browser->page(),
                     SIGNAL(menuBarVisibilityChangeRequested(bool)),
                     SLOT(menuBarVisibilityChangeRequested(bool))));
      assert(connect(browser->page(), SIGNAL(microFocusChanged()),
                     SLOT(microFocusChanged())));
      assert(connect(browser->page(), SIGNAL(printRequested(QWebFrame*)),
                     SLOT(printRequested(QWebFrame*))));
      assert(connect(browser->page(), SIGNAL(repaintRequested(const QRect&)),
                     SLOT(repaintRequested(const QRect&))));
      assert(connect(browser->page(),
                     SIGNAL(restoreFrameStateRequested(QWebFrame*)),
                     SLOT(restoreFrameStateRequested(QWebFrame*))));
      assert(connect(browser->page(),
                     SIGNAL(saveFrameStateRequested(QWebFrame*,
                                                    QWebHistoryItem*)),
                     SLOT(saveFrameStateRequested(QWebFrame*,
                                                  QWebHistoryItem*))));
      assert(connect(browser->page(),
                     SIGNAL(scrollRequested(int, int, const QRect&)),
                     SLOT(scrollRequested(int, int, const QRect&))));
      assert(connect(browser->page(), SIGNAL(selectionChanged()),
                     SLOT(selectionChanged())));
//       assert(connect(browser->page(), SIGNAL(statusBarMessage(const QString&)),
//                      SLOT(statusBarMessage(const QString&))));
      assert(connect(browser->page(),
                     SIGNAL(statusBarVisibilityChangeRequested(bool)),
                     SLOT(statusBarVisibilityChangeRequested(bool))));
      assert(connect(browser->page(),
                     SIGNAL(toolBarVisibilityChangeRequested(bool)),
                     SLOT(toolBarVisibilityChangeRequested(bool))));
      assert(connect(browser->page(),
                     SIGNAL(unsupportedContent(QNetworkReply*)),
                     SLOT(unsupportedContent(QNetworkReply*))));
      assert(connect(browser->page(), SIGNAL(windowCloseRequested()),
                     SLOT(windowCloseRequested())));
      // QNetworkAccessManager
      assert(connect(browser->page()->networkAccessManager(),
                     SIGNAL(authenticationRequired(QNetworkReply*,
                                                   QAuthenticator*)),
                     SLOT(authenticationRequired(QNetworkReply*,
                                                 QAuthenticator*))));
      assert(connect(browser->page()->networkAccessManager(),
                     SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&,
                                                 QAuthenticator*)),
                     SLOT(proxyAuthenticationRequired(const QNetworkProxy&,
                                               QAuthenticator*))));
      assert(connect(browser->page()->networkAccessManager(),
                     SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
                     SLOT(sslErrors(QNetworkReply*, const QList<QSslError>&))));
      _tabs->setCurrentIndex(_tabs->addTab(browser, tr("New Tab")));
      _tabs->setTabsClosable(_tabs->count()>1);
    }

    void newSettings() {
      _searchEngines->clear();
      for (Settings::SearchEngines::const_iterator
             it(_settings.searchEngines().begin());
           it!=_settings.searchEngines().end(); it++)
        if (QFile(":/icons/"+it.key()).exists())
          _searchEngines->addItem(QIcon(":/icons/"+it.key()), "", it.value());
        else
          _searchEngines->addItem(it.key(), it.value());
      if (_searchEngines->count()==0)
        _searchEngines->addItem(QIcon(":/icons/gg"), tr("gg"),
                                tr("http://www.google.com/search?hl=%2&q=%1"));
      _searchEngines->setVisible(_searchEngines->count()>1);
    }

    void on_actionAddBookmark_triggered() {
      QComboBox* url(qobject_cast<QComboBox*>(_url));
      if (!_bookmarks) return;
      if (_url) {
        url->addItem(url->currentText());
        QAction* a(_bookmarks->addAction(url->currentText()));
        a->setData(url->currentText());
        connect(a, SIGNAL(triggered(bool)), SLOT(loadFromHistory()));
        saveBookmarks();
      }
    }
    
    void smaller() {
      _zoom->setValue(_zoom->value()-_zoom->pageStep());
    }
    
    void larger() {
      _zoom->setValue(_zoom->value()+_zoom->pageStep());
    }
    
    void zoom(int i) {
      LOG<<i;
      statusBar()->showMessage(tr("Zoom: %1%").arg(i));
      qobject_cast<QWebView*>(_tabs->currentWidget())->setZoomFactor(i/100.0);
    }

    void  loadFromHistory() {
      load(qobject_cast<QAction*>(sender())->data().toString());
    }
    
    void on_actionHome_activated() {
      LOG;
      load(_home);
    }
    
    void on_actionNew_triggered() {
      LOG;
      QStringList args(QCoreApplication::arguments());
      QString prg(args.takeFirst());
      statusBar()->showMessage(tr("opening new window"), 5000);
      QProcess::startDetached(prg, args);
    }

    void on_actionNewTab_triggered() {
      newTab();
    }

    void on_actionCloseTab_triggered() {
      on__tabs_tabCloseRequested(_tabs->currentIndex());
    }

    void on__tabs_currentChanged(int) {
      if (qobject_cast<QComboBox*>(_url)) {
        qobject_cast<QComboBox*>(_url)->setEditText
          (qobject_cast<QWebView*>(_tabs->currentWidget())
           ->url().toString());
        qobject_cast<QComboBox*>(_url)->lineEdit()->setCursorPosition(0);
      } else {
        qobject_cast<QLineEdit*>(_url)->setText
          (qobject_cast<QWebView*>(_tabs->currentWidget())
           ->url().toString());
        qobject_cast<QLineEdit*>(_url)->setCursorPosition(0);
      }
      activateTab();
    }
    
    void on__tabs_tabCloseRequested(int index) {
      _tabs->removeTab(index);
      _tabs->setTabsClosable(_tabs->count()>1);
    }

    void on_actionPrintPreview_triggered() {
      QPrintPreviewDialog preview(&_printer, this);
      connect(&preview, SIGNAL(paintRequested(QPrinter*)),
              qobject_cast<QWebView*>(_tabs->currentWidget()),
              SLOT(print(QPrinter*)));
      preview.exec();
    }
    
    void on_actionInstantPrint_triggered() {
      qobject_cast<QWebView*>(_tabs->currentWidget())->print(&_printer);
    }
    
    void on_actionPrint_triggered() {
      QPrintDialog dialog(&_printer, this);
      dialog.setWindowTitle(tr("Print Document"));
      if (dialog.exec()!=QDialog::Accepted) return;
      on_actionInstantPrint_triggered();
    }

    void on_actionClose_triggered() {
      LOG;
      close();
    }

    void on_actionBack_triggered() {
      LOG;
      qobject_cast<QWebView*>(_tabs->currentWidget())->history()->back();
    }

    void on_actionBack_hovered() {
      LOG;
      if (!qobject_cast<QWebView*>(_tabs->currentWidget())->history()
          ->backItem().isValid())
        return;
      actionBack->setStatusTip
        (tr("%1 - %2", "statusbar actionBack_hovered %1=url %2=title")
         .arg(qobject_cast<QWebView*>(_tabs->currentWidget())->history()
              ->backItem().url().toString())
         .arg(qobject_cast<QWebView*>(_tabs->currentWidget())->history()
              ->backItem().title()));
      actionBack->showStatusText(this);
    }

    void on_actionForward_triggered() {
      LOG;
      qobject_cast<QWebView*>(_tabs->currentWidget())->history()->forward();
    }

    void on_actionForward_hovered() {
      LOG;
      if (!qobject_cast<QWebView*>(_tabs->currentWidget())->history()
          ->forwardItem().isValid())
        return;
      actionForward->setStatusTip
        (tr("%1 - %2", "statusbar actionForward_hovered %1=url %2=title")
         .arg(qobject_cast<QWebView*>(_tabs->currentWidget())->history()
              ->forwardItem().url().toString())
         .arg(qobject_cast<QWebView*>(_tabs->currentWidget())->history()
              ->forwardItem().title()));
      actionForward->showStatusText(this);
    }

    void on_actionReload_triggered() {
      LOG;
      qobject_cast<QWebView*>(_tabs->currentWidget())->reload();
    }

    void on_actionStop_triggered() {
      LOG;
      for (int i(0); i<_tabs->count(); ++i)
        qobject_cast<QWebView*>(_tabs->widget(i))->stop();
      _downloadManager->abort();
    }

    void on_actionClearLocation_triggered() {
      if (!qobject_cast<QComboBox*>(_url)) return;
      qobject_cast<QComboBox*>(_url)->clearEditText();
      qobject_cast<QComboBox*>(_url)->setFocus();
    }

    void on_actionClearFind_triggered() {
      _find->clear();
      _find->setFocus();
    }

    void on_actionFind_triggered() {
      if (_find->isHidden()) {
        _find->setText(qobject_cast<QWebView*>(_tabs->currentWidget())
                       ->selectedText());
        _find->show();
      }
      disconnect(_find, SIGNAL(returnPressed()), this, SLOT(rfind()));
      disconnect(_find, SIGNAL(textEdited(QString)), this, SLOT(rfind()));
      assert(connect(_find, SIGNAL(returnPressed()), SLOT(find())));
      assert(connect(_find, SIGNAL(textEdited(QString)), SLOT(find())));
      _find->changeStyleSheet(tr("background-color: white", "neutral find"));
      _find->setFocus();
    }

    void on_actionReverseFind_triggered() {
      if (_find->isHidden()) {
        _find->setText(qobject_cast<QWebView*>(_tabs->currentWidget())
                       ->selectedText());
        _find->show();
      }
      disconnect(_find, SIGNAL(returnPressed()), this, SLOT(find()));
      disconnect(_find, SIGNAL(textEdited(QString)), this, SLOT(find()));
      assert(connect(_find, SIGNAL(returnPressed()), SLOT(rfind())));
      assert(connect(_find, SIGNAL(textEdited(QString)), SLOT(rfind())));
      _find->changeStyleSheet(tr("background-color: white", "neutral find"));
      _find->setFocus();
    }

    void find(const QString& txt=QString()) {
      if (qobject_cast<QWebView*>(_tabs->currentWidget())->findText
          (_find->text(),
           QWebPage::FindWrapsAroundDocument))
        _find->changeStyleSheet(tr("background-color: #ADA", "text found"));
      else
        _find->changeStyleSheet(tr("background-color: #F77", "text not found"));
    }

    void rfind(const QString& txt=QString()) {
      if(qobject_cast<QWebView*>(_tabs->currentWidget())->findText
         (_find->text(),
          QWebPage::FindWrapsAroundDocument
          |QWebPage::FindBackward))
        _find->changeStyleSheet(tr("background-color: #ADA", "text found"));
      else
        _find->changeStyleSheet(tr("background-color: #F77", "text not found"));
    }

    void on_actionUnFind_triggered() {
      _find->hide();
    }
    
    void on_actionSearch_triggered() {
      load(_searchEngines->itemData(_searchEngines->currentIndex()).toString()
           .arg(_search->text())
           .arg(QLocale::system().name().left(2)));
    }

    void on_actionClearSearch_triggered() {
      _search->clear();
    }

    void on_actionProxy_triggered() {
      _proxy.show();
    }

    void on_actionLoginCertificate_triggered() {
      _logincertificate.show();
    }

    void on_actionErrorLog_triggered() {
      _errorLog.show();
    }
    
    void on_actionAbout_triggered() {
      QMessageBox::about(this, tr("About"),
                         tr("%8\n"
                            "Version: %1\n"
                            "Builddate: %2\n"
                            "Libraries:\n"
                            "%3\n"
                            "%4\n"
                            "qt-%5 (%6)\n"
                            "openssl-%7")
                         .arg(VERSION)
                         .arg(BUILDDATE)
                         .arg(QString::fromStdString(proxy::version()))
                         .arg(QString::fromStdString(pcsc::version()))
                         .arg(qVersion())
                         .arg(QT_VERSION_STR)
                         .arg(SHLIB_VERSION_NUMBER)
                         .arg(QApplication::applicationName()));
    }
    
    void on_actionSettings_triggered() {
      _settings.show();
    }

    void on_actionSaveWindowState_triggered() {
      saveWin();
    }

    void on_actionLoadWindowState_triggered() {
      loadWin();
    }

    void saveWin() {
      LOG<<"Save Window State";
      saveBookmarks();
      if (!_settings()) return;
      QStringList tabs;
      for (int i(0); i<_tabs->count(); ++i)
        tabs<<qobject_cast<QWebView*>(_tabs->widget(i))->url().toString();
      _settings()->setValue("Window/Tabs", tabs);
      _settings()->setValue("Window/CurrentTab", _tabs->currentIndex());
      _settings()->setValue("Window/Geometry", saveGeometry());
      _settings()->setValue("Window/WindowState", saveState());
    }

    void saveBookmarks() {
      LOG<<"Saving Bookmarks ...";
      QStringList urls;
      if (qobject_cast<QComboBox*>(_url))
        for (int i(0); i<qobject_cast<QComboBox*>(_url)->count(); ++i)
          urls<<qobject_cast<QComboBox*>(_url)->itemText(i);
      if (_bookmarkfile.writeable()) {
        LOG<<"write to bookmark file";
        _bookmarkfile.write(urls);
      } else if (_settings()) {
        LOG<<"write to settings";
        _settings()->setValue("Window/Urls", urls);
      } else LOG<<"bookmarks not saved";
    }

    void loadWin() {
      LOG;
      loadBookmarks();
      if (!_settings()) return;
      QStringList tabs(_settings()->value("Window/Tabs").toStringList());
      int oldCnt(_tabs->count());
      for (QStringList::iterator it(tabs.begin()); it!=tabs.end(); ++it) {
        LOG<<"-------------------------------------------------------------";
        load(*it, newTab());
        LOG<<"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
      }
      for (int i(0); i<oldCnt && _tabs->count()>1; ++i) _tabs->removeTab(0);
      _tabs->setTabsClosable(_tabs->count()>1);
      _tabs->setCurrentIndex(_settings()->value("Window/CurrentTab").toInt());
      restoreGeometry(_settings()->value("Window/Geometry").toByteArray());
      restoreState(_settings()->value("Window/WindowState").toByteArray());
    }

    void loadBookmarks() {
      LOG<<"Loading Bookmarks ...";
      QStringList urls;
      if (_bookmarkfile.readable()) {
        LOG<<"load from file";
        urls = _bookmarkfile.read();
      } else if (_settings()) {
        LOG<<"load from settings";
        urls = _settings()->value("Window/Urls").toStringList();
      }
      if (qobject_cast<QComboBox*>(_url)) {
        QString ci(qobject_cast<QComboBox*>(_url)->currentText());
        qobject_cast<QComboBox*>(_url)->clear();
        qobject_cast<QComboBox*>(_url)->addItems(urls);
        qobject_cast<QComboBox*>(_url)->lineEdit()->setText(ci);
      }
      if (_bookmarks) {
        _bookmarks->clear();
        for (QStringList::iterator it(urls.begin()); it!=urls.end(); ++it) {
          QAction* a(_bookmarks->addAction(*it));
          a->setData(*it);
          connect(a, SIGNAL(triggered(bool)), SLOT(loadFromHistory()));
        }
      }
    }
    
    //@name                                                       QWebView slots
    //@{
    
    void urlChanged(const QUrl& url) {
      LOG<<url.toString();
      if (sender()!=_tabs->currentWidget()) return;
      LOG<<"signal on current tab";
      if (qobject_cast<QComboBox*>(_url)) {
        qobject_cast<QComboBox*>(_url)->setEditText(url.toString());
        qobject_cast<QComboBox*>(_url)->lineEdit()->setCursorPosition(0);
      } else {
        qobject_cast<QLineEdit*>(_url)->setText(url.toString());
        qobject_cast<QLineEdit*>(_url)->setCursorPosition(0);
      }
    }
    
    void linkClicked(const QUrl& url) {
      LOG<<url.toString();
      load(url, qobject_cast<QWebView*>(sender()));
    }
    
    void iconChanged() {
      LOG;
      QWebView* view(qobject_cast<QWebView*>(sender()));
      int index = _tabs->indexOf(view);
      if (index<0) return;
      LOG<<"get icon for "<<view->url();
      QIcon icon(QWebSettings::iconForUrl(view->url()));
      if (!icon.isNull()) return _tabs->setTabIcon(index, icon);
      LOG<<"icon for url is null"<<view->url();
      _tabs->setTabIcon(index, QWebSettings::webGraphic
                        (QWebSettings::DefaultFrameIconGraphic));
    }
    
    void titleChanged(const QString& text) {
      LOG<<text;
      _tabs->setTabText(_tabs->indexOf(qobject_cast<QWidget*>(sender())),
                        trUtf8("%1").arg(text));
    }
    
    void statusBarMessage(const QString& text) {
      LOG<<text;
      if (text.size()) statusBar()->showMessage(tr("Info: %1").arg(text));
    }
    
    void loadProgress(int i) {
      LOG<<i;
    }
    
    void loadStarted() {
      LOG;
    }
    
    void loadFinished(bool ok) {
      LOG<<(ok?"success":"error");
      QSize sz(qobject_cast<QWebView*>(_tabs->currentWidget())->size());
      qobject_cast<QWebView*>(_tabs->currentWidget())->resize(0,0);
      qobject_cast<QWebView*>(_tabs->currentWidget())->resize(sz);
      if (ok) {
        statusBar()->showMessage(tr("done."));
        goodUrl();
      }
      activateTab();
    }

    //@}

    //@name                                             QWebPage WebAction slots
    //@{
    
    void triggeredOpenLink(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredOpenLinkInNewWindow(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredOpenFrameInNewWindow(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredDownloadLinkToDisk(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredCopyLinkToClipboard(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredOpenImageInNewWindow(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredDownloadImageToDisk(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredCopyImageToClipboard(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredBack(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredForward(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredStop(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredReload(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredCut(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredCopy(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredPaste(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredUndo(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredRedo(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToNextChar(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToPreviousChar(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToNextWord(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToPreviousWord(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToNextLine(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToPreviousLine(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToStartOfLine(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToEndOfLine(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToStartOfBlock(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToEndOfBlock(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToStartOfDocument(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredMoveToEndOfDocument(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectNextChar(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectPreviousChar(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectNextWord(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectPreviousWord(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectNextLine(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectPreviousLine(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectStartOfLine(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectEndOfLine(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectStartOfBlock(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectEndOfBlock(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectStartOfDocument(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectEndOfDocument(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredDeleteStartOfWord(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredDeleteEndOfWord(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSetTextDirectionDefault(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSetTextDirectionLeftToRight(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSetTextDirectionRightToLeft(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredToggleBold(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredToggleItalic(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredToggleUnderline(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredInspectElement(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredInsertParagraphSeparator(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredInsertLineSeparator(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    void triggeredSelectAll(bool) {
      LOG<<qobject_cast<QAction*>(sender())->data().toString();
    }
    
    //@}

    //@name                                                       QWebPage slots
    //@{
    
    void contentsChanged() {
      LOG;
    }
    
    void databaseQuotaExceeded(QWebFrame* frame, QString databaseName) {
      LOG<<databaseName;
    }
    
    void downloadRequested(const QNetworkRequest& request) {
      LOG;
    }
    
    void frameCreated(QWebFrame* frame) {
      LOG;
    }
    
    void geometryChangeRequested(const QRect& geom) {
      LOG;
    }
    
//     void linkClicked(const QUrl& url) {
//       LOG<<url.toString();
//     }
    
    void linkHovered(const QString& link, const QString& title,
                     const QString& textContent) {
      LOG<<link<<title<<textContent;
      statusBar()->showMessage(tr("%1", "statusbar for hovered link %1=url")
                               .arg(link));
    }
    
//     void loadFinished(bool ok) {
//       LOG<<(ok?"succeess":"error");
//     }
    
//     void loadProgress(int progress) {
//       LOG<<progress;
//     }
    
//     void loadStarted() {
//       LOG;
//     }
    
    void menuBarVisibilityChangeRequested(bool visible) {
      LOG;
    }
    
    void microFocusChanged() {
      LOG;
    }
    
    void printRequested(QWebFrame* frame) {
      LOG;
    }
    
    void repaintRequested(const QRect& dirtyRect) {
      //LOG;
    }
    
    void restoreFrameStateRequested(QWebFrame* frame) {
      LOG;
    }
    
    void saveFrameStateRequested(QWebFrame* frame, QWebHistoryItem* item) {
      LOG;
    }
    
    void scrollRequested(int dx, int dy, const QRect& rectToScroll) {
      LOG;
    }
    
    void selectionChanged() {
      LOG;
    }
    
//     void statusBarMessage(const QString& text) {
//       LOG;
//     }
    
    void statusBarVisibilityChangeRequested(bool visible) {
      LOG;
    }
    
    void toolBarVisibilityChangeRequested(bool visible) {
      LOG;
    }

    //@name                                                     handle downloads
    //@{
    
    void unsupportedContent(QNetworkReply* reply) {
      LOG<<reply->header(QNetworkRequest::ContentTypeHeader).toString();
      LOG<<"Status:"<<_downloadManager->networkError(reply->error());
      QList<QNetworkReply::RawHeaderPair> rh(reply->rawHeaderPairs());
      for(QList<QNetworkReply::RawHeaderPair>::iterator it(rh.begin());
          it!=rh.end(); ++it) {
        LOG<<"RawHeader:"<<it->first<<it->second;
      }
      if (reply->isFinished()) {
        handleContent(reply);
      } else {
        assert(connect(reply, SIGNAL(finished()), SLOT(downloadFinished())));
      }
    }

    void downloadFinished() {
      LOG;
      QNetworkReply *reply(qobject_cast<QNetworkReply*>(sender()));
      handleContent(reply);
    }

    //! check and handle content that is defined in settings
    void metaDataChanged(QNetworkReply* reply) {
      QString filename
        (QString::fromUtf8(reply->rawHeader("Content-Disposition")));
      if (filename.contains
          (QRegExp("^\\s*attachment\\s*;\\s*filename\\s*=\\s*\"[^\"]+\""))) {
        LOG<<"From Content-Disposition";
        filename = filename.replace
          (QRegExp("^\\s*attachment\\s*;\\s*filename\\s*=\\s*\"([^\"]+)\".*"),
           "\\1");
      } else {
        LOG<<"From path";
        filename =
          QFileInfo(!reply->url().toLocalFile().isEmpty()
                    ?reply->url().toLocalFile()
                    :reply->url().path()).fileName();
      }
      LOG<<"Filename:"<<filename;
      QStringList type
        (_settings.mimetype
         (reply->header(QNetworkRequest::ContentTypeHeader).toString(),
          filename));
    }

    void handleContent(QNetworkReply* reply) {
      LOG<<"Location:"<<reply->header(QNetworkRequest::LocationHeader)
        .toString();
      LOG<<"Content-Type:"<<reply->header(QNetworkRequest::ContentTypeHeader)
        .toString();
      LOG<<"Content-Disposition:"<<reply->rawHeader("Content-Disposition");
      LOG<<"Status:"<<_downloadManager->networkError(reply->error());
      LOG<<"URL:"<<reply->url().toString();
      LOG<<"File:"<<reply->url().toLocalFile();
      LOG<<"Path:"<<reply->url().path();
      if (reply->error()!=QNetworkReply::NoError) {
        LOG<<"Error:"<<_downloadManager->networkError(reply->error());
        return;
      }
      QString filename
        (QString::fromUtf8(reply->rawHeader("Content-Disposition")));
      if (filename.contains
          (QRegExp("^\\s*attachment\\s*;\\s*filename\\s*=\\s*\"[^\"]+\""))) {
        LOG<<"From Content-Disposition";
        filename = filename.replace
          (QRegExp("^\\s*attachment\\s*;\\s*filename\\s*=\\s*\"([^\"]+)\".*"),
           "\\1");
      } else {
        LOG<<"From path";
        filename =
          QFileInfo(!reply->url().toLocalFile().isEmpty()
                    ?reply->url().toLocalFile()
                    :reply->url().path()).fileName();
      }
      LOG<<"Filename:"<<filename;
      QStringList type
        (_settings.mimetype
         (reply->header(QNetworkRequest::ContentTypeHeader).toString(),
          filename));
      if (!type.isEmpty()) {
        LOG<<"Start Application";
        filename.replace(QRegExp("^(.*)\\."+type.at(0)+"$"),
                         "\\1"); // remove extension
        run(reply, filename+"."+type.at(0), type.at(1));
      } else {
        LOG<<"Show SaveOrRunDialog";
        SaveOrRunDialog choice(_kiosk, this);
        choice.setup(filename,
                     reply->header(QNetworkRequest::ContentTypeHeader)
                     .toString(),
                     reply->url().host());
        if (choice.exec()==QDialog::Accepted) {
          if (choice.save()) {
            QFile file(choice.sor()->filename());
            file.open(QIODevice::WriteOnly);
            file.write(reply->readAll());
            file.close();
          } else if (choice.run()) {
            run(reply, filename, choice.sor()->program()+" %1");
          } else {
            // ...
          }
        }
      }
    }

    void run(QNetworkReply* reply, QString filename, QString command) {
      TemporaryFile *file(new TemporaryFile
                            (QDir::tempPath()+QDir::separator()
                             +filename));
      file->open();
      reply->seek(0);
      file->write(reply->readAll());
      file->close();
      LOG<<"Stored as:"<<file->fileName();
      statusBar()->showMessage(tr("launching application ..."), 5000);
      QProcess* process(new QProcess);
      _downloadProcesses[process] = file;
      assert(connect(process, SIGNAL(finished(int, QProcess::ExitStatus)),
                     SLOT(processFinished())));
      QStringList args(command.split(" ")
                       .replaceInStrings("%1", file->fileName()));
      QString prg(args.takeFirst());
      LOG<<"Running:"<<prg<<args.join(" ");
      process->start(prg, args);
    }

    void processFinished() {
      LOG;
      if (_downloadProcesses.find(qobject_cast<QProcess*>(sender()))
          == _downloadProcesses.end()) return;
      if (_downloadProcesses[qobject_cast<QProcess*>(sender())])
        _downloadProcesses[qobject_cast<QProcess*>(sender())]
          ->setAutoRemove(true);
      delete _downloadProcesses[qobject_cast<QProcess*>(sender())];
      _downloadProcesses.erase(qobject_cast<QProcess*>(sender()));
    }

    //@}
    
    void windowCloseRequested() {
      LOG;
    }
    
    //@}

    //@name                                              DownloadManager signals
    //@{

    void progress(qint64 done, qint64 total) {
      _progress->setMaximum(total);
      _progress->setValue(done);
    }

    void started() {
      QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
      actionStop->setVisible(true);
      actionReload->setVisible(false);
      _progress->setRange(0, 0);
      _progress->setValue(0);
      _progress->setEnabled(true);
      _progress->show();
    }
    
    void finished() {
      LOG;
      QApplication::restoreOverrideCursor();
      actionStop->setVisible(false);
      actionReload->setVisible(true);
      _progress->setRange(0, 1);
      _progress->setValue(1);
      _progress->setEnabled(false);
    }

    void finished(QNetworkReply *r) {
      LOG;
      if (r->error()!=QNetworkReply::NoError &&
          r->error()!=QNetworkReply::OperationCanceledError) {
        statusBar()->showMessage(DownloadManager::networkError(r->error()));
        badUrl();
        if (!_showErrorLog) {
          statusBar()->addPermanentWidget
            ((_showErrorLog = new QPushButton(QIcon(":/icons/error"),
                                              tr("errors", "show error log"))));
          assert(connect(_showErrorLog, SIGNAL(clicked(bool)),
                         actionErrorLog, SLOT(trigger())));
        }
        _showErrorLog->show();
      }
    }

    void errorReset() {
      LOG;
      _showErrorLog->hide();
    }

    void badUrl() {
      if (qobject_cast<QComboBox*>(_url))
        dynamic_cast<ButtonLineEdit*>
          (qobject_cast<QComboBox*>(_url)->lineEdit())
          ->changeStyleSheet(tr("background-color: #F77", "invalid url"));
      else
        qobject_cast<QLineEdit*>(_url)
          ->setStyleSheet(tr("background-color: #F77", "invalid url"));
    }

    void goodUrl() {
      if (qobject_cast<QComboBox*>(_url))
        dynamic_cast<ButtonLineEdit*>
          (qobject_cast<QComboBox*>(_url)->lineEdit())
          ->changeStyleSheet(tr("background-color: white", "valid url"));
      else
        qobject_cast<QLineEdit*>(_url)
          ->setStyleSheet(tr("background-color: white", "valid url"));
    }

    void downloadError(QString error) {
      _errorLog.append(error);
    }

    //@}
    
    //@name                                        QNetworkAccessManager signals
    //@{

    void authenticationRequired(QNetworkReply* reply,
                                QAuthenticator* authenticator) {
      LOG<<reply->url().toString()<<authenticator->realm();
      statusBar()->showMessage(tr("authentication required"));
      Authentication auth(authenticator, this);
      if (auth.exec()==QDialog::Rejected) reply->abort();
    }
    
    void proxyAuthenticationRequired(const QNetworkProxy& proxy,
                                     QAuthenticator* authenticator) {
      LOG;
    }
    
    void sslErrors(QNetworkReply* reply, const QList<QSslError>& errors) {
      LOG;
      statusBar()->showMessage(tr("ssl error"));
    }
    
    //@}

  private:

    void activateTab() {
      iconChanged();
      QWebHistory* history(qobject_cast<QWebView*>(_tabs->currentWidget())
                          ->history());
      actionForward->setEnabled(history->canGoForward());
      actionBack->setEnabled(history->canGoBack());
      _history->clear();
      QList<QWebHistoryItem> items(history->items());
      for (int i = 0; i < items.size(); ++i) {
        QAction* a(_history->addAction(items[i].title()));
        a->setData(items[i].url());
        connect(a, SIGNAL(triggered(bool)), SLOT(loadFromHistory()));
      }
    }

  private:
    
    QWidget* _url;
    ButtonLineEdit* _find;
    QSlider* _zoom;
    QProgressBar* _progress;
    QString _home;
    bool _kiosk;
    QPrinter _printer;
    SslClientAuthNetworkAccessManager _networkManager;
    QSharedPointer<DownloadManager> _downloadManager;
    typedef std::map<QProcess*, TemporaryFile*> DownloadProcesses;
    DownloadProcesses _downloadProcesses;
    Settings _settings;
    ErrorLog _errorLog;
    LoginCertificate _logincertificate;
    gui::Proxy _proxy;
    QPushButton* _showErrorLog;
    std::auto_ptr<EditBookmarks> _editbookmarks;
    bool _startUrl;
    bool _quirks;
    ButtonLineEdit* _search;
    QComboBox* _searchEngines;
    FileStorage _bookmarkfile;
};

//! @}
#endif
