/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009, 2010, 2011, 2012 Alexis Megas, Bernd Stramm,
** Gunther van Dooble, and the Dooble Team.
** All rights reserved.
**
** License: GPL2 only:
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; version 2 of the License only.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
** or see here: http://www.gnu.org/licenses/gpl.html
**
** For the WebKit library, please see: http://webkit.org.
**
** THE CODE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
** IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS APPLICATION, EVEN IF ADVISED
** OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Please report all praise, requests, bugs, and problems to the project
** team and administrators: http://sf.net/projects/dooble.
**
** You can find us listed at our project page. New team members are welcome.
** The name of the authors should not be used to endorse or promote products
** derived from Dooble without specific prior written permission.
** If you use this code for other projects, please let us know.
**
** Web sites:
**   http://sf.net/projects/dooble
**   http://dooble.sf.net
****************************************************************************/

#include <string>
#include <iostream>

extern "C"
{
#include <fcntl.h>
#include <signal.h>
}

#include <QDir>
#include <QDebug>
#include <QTimer>
#include <QtCore>
#include <QBuffer>
#include <QPrinter>
#include <QProcess>
#include <QLineEdit>
#ifdef Q_WS_MAC
#include <QMacStyle>
#endif
#include <QSettings>
#include <QSplitter>
#include <QSqlQuery>
#include <QWebFrame>
#include <QClipboard>
#include <QTextCodec>
#include <QCloseEvent>
#include <QFileDialog>
#include <QMessageBox>
#include <QTranslator>
#include <QApplication>
#include <QPaintEngine>
#include <QPrintDialog>
#include <QSqlDatabase>
#include <QPluginLoader>
#include <QNetworkReply>
#include <QWidgetAction>
#include <QAuthenticator>
#include <QDesktopWidget>
#include <QWebHistoryItem>
#include <QDesktopServices>
#include <QCoreApplication>
#include <QFileIconProvider>
#include <QPrintPreviewDialog>

#include "dmisc.h"
#include "dooble.h"
#include "dhistory.h"
#include "dwebpage.h"
#include "dwebview.h"
#include "derrorlog.h"
#include "dplugintab.h"
#include "dnetworkcache.h"
#include "dbookmarkspopup.h"
#include "dbookmarkswindow.h"
#include "dclearcontainers.h"
#include "dreinstatedooble.h"
#include "dpagesourcewindow.h"
#include "ui_passwordPrompt.h"
#include "ui_passphrasePrompt.h"
#include "dnetworkaccessmanager.h"

using namespace simpleplugin;

int dooble::s_instances = 0;
QPointer<QMenu> dooble::s_bookmarksPopupMenu = 0;
QUuid dooble::s_id = QUuid::createUuid();
QMutex dooble::s_saveHistoryMutex;
QString dooble::s_homePath = "";
QPointer<dcookies> dooble::s_cookies = 0;
QPointer<dhistory> dooble::s_historyWindow = 0;
QPointer<derrorlog> dooble::s_errorLog = 0;
QPointer<dsettings> dooble::s_settingsWindow = 0;
QPointer<dcookiewindow> dooble::s_cookieWindow = 0;
QPointer<dhistorymodel> dooble::s_historyModel = 0;
QPointer<dnetworkcache> dooble::s_networkCache = 0;
QPointer<ddownloadwindow> dooble::s_downloadWindow = 0;
QPointer<dbookmarkspopup> dooble::s_bookmarksPopup = 0;
QPointer<dbookmarkswindow> dooble::s_bookmarksWindow = 0;
QPointer<dclearcontainers> dooble::s_clearContainersWindow = 0;
QPointer<dexceptionswindow> dooble::s_dntWindow = 0;
QPointer<dexceptionswindow> dooble::s_popupsWindow = 0;
QPointer<dexceptionswindow> dooble::s_adBlockWindow = 0;
QPointer<dexceptionswindow> dooble::s_imageBlockWindow = 0;
QPointer<dexceptionswindow> dooble::s_cookiesBlockWindow = 0;
QPointer<dexceptionswindow> dooble::s_httpRedirectWindow = 0;
QPointer<dexceptionswindow> dooble::s_httpReferrerWindow = 0;
QPointer<dexceptionswindow> dooble::s_cacheExceptionsWindow = 0;
QPointer<dexceptionswindow> dooble::s_javaScriptExceptionsWindow = 0;
QPointer<QStandardItemModel> dooble::s_bookmarksFolderModel = 0;
QHash<QString, int> dooble::s_mostVisitedHosts;
QMap<QString, QString> dooble::s_applicationsActions;
QHash<QString, QVariant> dooble::s_settings;

static char *s_crashFileName = 0;

static void sig_handler(int signum)
{
  if(signum == SIGTERM)
    dmisc::removeRestorationFiles(dooble::s_id);
  else
    {
      /*
      ** We shall create a .crashed file in the user's Dooble directory.
      ** Upon restart of Dooble, Dooble will query the Histories directory and
      ** adjust itself accordingly if the .crashed file exists.
      */

      if(s_crashFileName)
	creat(s_crashFileName, O_CREAT);
    }

  _exit(EXIT_FAILURE);
}

static void qt_message_handler(QtMsgType type, const char *msg)
{
  Q_UNUSED(type);
  dmisc::logError(msg);
}

int main(int argc, char *argv[])
{
  /*
  ** See http://trac.webkit.org/wiki/Fingerprinting#vii.DateObject.
  */

  qputenv("TZ", ":UTC");

  /*
  ** Prepare signal handlers. Clearly, this does not account for
  ** the user's desire to restore sessions.
  */

  QList<int> l_signals;
#ifdef Q_OS_UNIX
  struct sigaction act;
#endif
  l_signals << SIGABRT
#ifdef Q_OS_UNIX
	    << SIGBUS
#endif
	    << SIGFPE
	    << SIGILL
	    << SIGINT
#ifdef Q_OS_UNIX
	    << SIGKILL
	    << SIGQUIT
#endif
	    << SIGSEGV
	    << SIGTERM;

  while(!l_signals.isEmpty())
    {
#ifndef Q_OS_UNIX
      signal(l_signals.takeFirst(), sig_handler);
#else
      act.sa_handler = sig_handler;
      sigemptyset(&act.sa_mask);
      act.sa_flags = 0;
      sigaction(l_signals.takeFirst(), &act, (struct sigaction *) 0);
#endif
    }

#ifdef Q_OS_UNIX
  /*
  ** Ignore SIGPIPE. Some plugins cause Dooble to die.
  */

  act.sa_handler = SIG_IGN;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
  sigaction(SIGPIPE, &act, (struct sigaction *) 0);
#endif

  /*
  ** Prepare the style before creating a QApplication object, per Qt.
  */

#ifdef Q_WS_MAC
  QApplication::setStyle(new QMacStyle());
#endif

  /*
  ** Proceed to Qt initialization.
  */

  qInstallMsgHandler(qt_message_handler);

  QApplication qapp(argc, argv);

  /*
  ** The Error Log is quite important. It needs to be created
  ** right after the QApplication object.
  */

  dooble::s_errorLog = new derrorlog();

  /*
  ** The initializeCrypt() method must be called as soon as possible.
  */

  dmisc::initializeCrypt();

  /*
  ** Process command-line arguments.
  */

  QStringList urls;
  QHash<QString, QVariant> argumentsHash;

  if(argc > 1)
    {
      int rc = -1;
      QString usage("");

      usage.append("Usage: Dooble [OPTION]...\n\n");
      usage.append("Options:\n");
      usage.append("--full-screen-mode\n");

      for(int i = 1; i < argc; i++)
	{
	  QString option(argv[i]);

	  if(option == "--full-screen-mode")
	    argumentsHash["full-screen-mode"] = true;
	  else
	    {
	      if(QUrl::fromUserInput(argv[i]).isValid())
		{
		  QUrl url(QUrl::fromUserInput(argv[i]));

		  urls.append(url.toString(QUrl::StripTrailingSlash));
		}
	      else
		std::cerr << "Invalid URL: "
			  << argv[i]
			  << ". Skipping." << std::endl;
	    }
	}

      if(rc + 1 > 0)
	{
	  std::cout << usage.toStdString() << std::endl;
	  return rc;
	}
    }

  /*
  ** Configure translations.
  */

  QTranslator qtTranslator;

  qtTranslator.load("qt_" + QLocale::system().name(), "Translations");
  qapp.installTranslator(&qtTranslator);

  QTranslator myappTranslator;

  myappTranslator.load("dooble_" + QLocale::system().name(),
		       "Translations");
  qapp.installTranslator(&myappTranslator);

#ifdef Q_WS_WIN
  QDir homeDir(QDir::current());
  QString username("");
  QFileInfo fileInfo(homeDir.absolutePath());
  QByteArray tmp(qgetenv("USERNAME"));

  if(!(fileInfo.isReadable() && fileInfo.isWritable()))
    {
      homeDir = QDir::home();
      homeDir.mkdir(".dooble");
    }
  else
    {
      username = QString(tmp);

      if(username.isEmpty())
	homeDir.mkdir(".dooble");
      else
	homeDir.mkdir(username + QDir::separator() + ".dooble");

      if(username.isEmpty())
	dooble::s_homePath = homeDir.absolutePath() +
	  QDir::separator() + ".dooble";
      else
	dooble::s_homePath = homeDir.absolutePath() + QDir::separator() +
	  username + QDir::separator() + ".dooble";
    }
#else
  QDir homeDir(QDir::home());

  homeDir.mkdir(".dooble");
  dooble::s_homePath = homeDir.absolutePath() +
    QDir::separator() + ".dooble";
#endif

  /*
  ** Disable Web page icons.
  */

  QWebSettings::globalSettings()->setIconDatabasePath("");

  /*
  ** Disable caches.
  */

  QWebSettings::globalSettings()->setMaximumPagesInCache(0);
  QWebSettings::globalSettings()->setOfflineStorageDefaultQuota(0);
  QWebSettings::globalSettings()->setOfflineWebApplicationCacheQuota(0);

  /*
  ** Other Web settings.
  */

  QWebSettings::globalSettings()->setWebGraphic(QWebSettings::MissingImageGraphic,
						QPixmap());

  /*
  ** Set application attributes.
  */

  QCoreApplication::setApplicationName("Dooble");
  QCoreApplication::setOrganizationName("Dooble");
  QCoreApplication::setOrganizationDomain("dooble.sf.net");
  QCoreApplication::setApplicationVersion(DOOBLE_VERSION_STR);
  QSettings::setPath(QSettings::IniFormat, QSettings::UserScope,
		     dooble::s_homePath);
  QSettings::setDefaultFormat(QSettings::IniFormat);

  /*
  ** Remove old configuration settings.
  */

  QSettings settings;

  settings.remove("bookmarksWindow/tableColumnsState");
  settings.remove("containers_cleared_v122");
  settings.remove("containers_cleared_v124");
  settings.remove("containers_cleared_v1231");
  settings.remove("containers_cleared_v127");
  settings.remove("containers_cleared_v132");
  settings.remove("historyWindow/tableColumnsState");
  settings.remove("historyWindow/tableColumnsState1");
  settings.remove("historyWindow/tableColumnsState2");
  settings.remove("historyWindow/tableColumnsState3");
  settings.remove("historyWindow/tableColumnsState4");
  settings.remove("historyWindow/tableColumnsState5");
  settings.remove("settingsWindow/globalFont");
  settings.remove("settingsWindow/hashType");
  settings.remove("settingsWindow/locationToolbarIconSizeIndex");
  settings.remove("settingsWindow/maximumPagesInCache");
  settings.remove("settingsWindow/setMaximumPagesInCache");
  settings.remove("settingsWindow/suppressHttpRedirect");
  settings.remove("settingsWindow/suppressHttpReferrer");

  if(!settings.contains("containers_cleared_v135"))
    {
      settings.remove("settingsWindow/ftpBrowsingProxyPort");
      settings.remove("settingsWindow/ftpDownloadProxyPort");
      settings.setValue("containers_cleared_v135", true);

      QStringList containers;

      containers << "adblockexceptions.db"
		 << "bookmarks.db"
		 << "cookies.db"
		 << "cookiesexceptions.db"
		 << "downloads.db"
		 << "favicons.db"
		 << "history.db"
		 << "javascriptexceptions.db"
		 << "popupsexceptions.db"
		 << "suppresshttpredirectexceptions.db"
		 << "suppresshttpreferrerexceptions.db";

      while(!containers.isEmpty())
	QFile::remove(dooble::s_homePath + QDir::separator() +
		      containers.takeFirst());
    }

  /*
  ** We need to set these before creating some of the support windows.
  */

  if(!settings.contains("iconSet"))
    settings.setValue("iconSet",
		      QString("%1/%2").arg(QDir::currentPath()).
		      arg("Icons/nuovext/configuration.cfg"));

  if(!QFileInfo(settings.value("iconSet").toString()).exists())
    settings.setValue("iconSet",
		      QString("%1/%2").arg(QDir::currentPath()).
		      arg("Icons/nuovext/configuration.cfg"));

  if(!settings.contains("settingsWindow/iconSet1"))
    settings.setValue("settingsWindow/iconSet1", settings.value("iconSet"));

  if(!QFileInfo(settings.value("settingsWindow/iconSet1").toString()).exists())
    settings.setValue("settingsWindow/iconSet1",
		      QString("%1/%2").arg(QDir::currentPath()).
		      arg("Icons/nuovext/configuration.cfg"));

  dooble::s_settings.clear();

  for(int i = 0; i < settings.allKeys().size(); i++)
    dooble::s_settings[settings.allKeys().at(i)] =
      settings.value(settings.allKeys().at(i));

  /*
  ** QWebSettings changes have to be performed after dooble::s_settings
  ** gets populated!
  */

  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::JavaEnabled,
     dooble::s_settings.value("settingsWindow/javaEnabled", false).toBool());
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::JavascriptEnabled,
     dooble::s_settings.value("settingsWindow/javascriptEnabled",
			      true).toBool());
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::JavascriptCanOpenWindows,
     dooble::s_settings.value("settingsWindow/javascriptEnabled",
			      true).toBool() &&
     dooble::s_settings.value("settingsWindow/javascriptAllowNewWindows",
			      true).toBool());
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::AutoLoadImages,
     dooble::s_settings.value("settingsWindow/automaticallyLoadImages",
			      true).toBool());
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::PluginsEnabled,
     dooble::s_settings.value("settingsWindow/enableWebPlugins",
			      true).toBool());
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::DnsPrefetchEnabled, true);
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::PrivateBrowsingEnabled, true);
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::SpatialNavigationEnabled,
     dooble::s_settings.value("settingsWindow/spatialNavigation",
			      false).toBool());
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::XSSAuditingEnabled,
     dooble::s_settings.value("settingsWindow/javascriptEnabled",
			      true).toBool() &&
     dooble::s_settings.value("settingsWindow/xssAuditingEnabled",
			      false).toBool());

  QString str(dooble::s_settings.value("settingsWindow/characterEncoding",
				       "").toString().toLower());
  QTextCodec *codec = 0;

  if((codec = QTextCodec::codecForName(str.toUtf8().constData())))
    QWebSettings::globalSettings()->setDefaultTextEncoding(codec->name());

  QFont font;

  if(!font.fromString
     (dooble::s_settings.value("settingsWindow/standardWebFont", "").
      toString()) || font.family().isEmpty())
#ifdef Q_WS_MAC
    font = QFont("Times", 16);
#elif defined Q_WS_WIN
    font = QFont("Serif", 16);
#else
    font = QFont("Serif", 16);
#endif

  if(font.pointSize() <= 0)
    font.setPointSize(16);

  font.setWeight(QFont::Normal);
  QWebSettings::globalSettings()->setFontFamily
    (QWebSettings::StandardFont, font.family());
  QWebSettings::globalSettings()->setFontSize
    (QWebSettings::DefaultFontSize, font.pointSize());

  if(!font.fromString(dooble::s_settings.value
		      ("settingsWindow/cursiveWebFont", "").
		      toString()) || font.family().isEmpty())
    font = QFont("Serif");

  font.setWeight(QFont::Normal);
  QWebSettings::globalSettings()->setFontFamily
    (QWebSettings::CursiveFont,
     font.family());

  if(!font.fromString(dooble::s_settings.value
		      ("settingsWindow/fantasyWebFont", "").
		      toString()) || font.family().isEmpty())
    font = QFont("Serif");

  font.setWeight(QFont::Normal);
  QWebSettings::globalSettings()->setFontFamily
    (QWebSettings::FantasyFont,
     font.family());

  if(!font.fromString(dooble::s_settings.value
		      ("settingsWindow/fixedWebFont", "").
		      toString()) || font.family().isEmpty())
#ifdef Q_WS_MAC
    font = QFont("Courier", 13);
#elif defined Q_WS_WIN
    font = QFont("Courier New", 16);
#else
    font = QFont("Courier", 10);
#endif

  if(font.pointSize() <= 0)
#ifdef Q_WS_MAC
    font.setPointSize(13);
#elif defined Q_WS_WIN
    font.setPointSize(16);
#else
    font.setPointSize(10);
#endif

  font.setWeight(QFont::Normal);
  QWebSettings::globalSettings()->setFontFamily
    (QWebSettings::FixedFont,
     font.family());
  QWebSettings::globalSettings()->setFontSize
    (QWebSettings::DefaultFixedFontSize,
     font.pointSize());

  if(!font.fromString(dooble::s_settings.value
		      ("settingsWindow/sansSerifWebFont", "").
		      toString()) || font.family().isEmpty())
#ifdef Q_WS_MAC
    font = QFont("Helvetica");
#elif defined Q_WS_WIN
    font = QFont("Arial");
#else
    font = QFont("Sans Serif");
#endif

  font.setWeight(QFont::Normal);
  QWebSettings::globalSettings()->setFontFamily
    (QWebSettings::SansSerifFont,
     font.family());

  if(!font.fromString(dooble::s_settings.value
		      ("settingsWindow/serifWebFont", "").
		      toString()) || font.family().isEmpty())
#ifdef Q_WS_MAC
    font = QFont("Times");
#elif defined Q_WS_WIN
    font = QFont("Times New Roman");
#else
    font = QFont("Serif");
#endif

  font.setWeight(QFont::Normal);
  QWebSettings::globalSettings()->setFontFamily
    (QWebSettings::SerifFont,
     font.family());

  int fontSize = dooble::s_settings.value("settingsWindow/minimumWebFontSize",
					  14).toInt();

  if(fontSize <= 0)
    fontSize = 14;

  QWebSettings::globalSettings()->setFontSize
    (QWebSettings::MinimumFontSize, fontSize);
  QWebSettings::globalSettings()->setObjectCacheCapacities(0, 0, 0);

  /*
  ** Initialize static members.
  */

  dooble::s_historyModel = new dhistorymodel();
  dooble::s_bookmarksFolderModel = new QStandardItemModel();
  dooble::s_dntWindow = new dexceptionswindow
    (new dexceptionsmodel("dntexceptions"));
  dooble::s_dntWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "DNT (Do Not Track) Exceptions"));
  dooble::s_popupsWindow = new dexceptionswindow
    (new dexceptionsmodel("popupsexceptions"));
  dooble::s_popupsWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "JavaScript Popups Exceptions"));
  dooble::s_adBlockWindow = new dexceptionswindow
    (new dexceptionsmodel("adblockexceptions"));
  dooble::s_adBlockWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "Third-Party Blocking Exceptions"));
  dooble::s_cookiesBlockWindow = new dexceptionswindow
    (new dexceptionsmodel("cookiesexceptions"));
  dooble::s_cookiesBlockWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "Cookies Exceptions"));
  dooble::s_httpRedirectWindow = new dexceptionswindow
    (new dexceptionsmodel("suppresshttpredirectexceptions"));
  dooble::s_httpRedirectWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "Suppress HTTP Redirect Exceptions"));
  dooble::s_httpReferrerWindow = new dexceptionswindow
    (new dexceptionsmodel("suppresshttpreferrerexceptions"));
  dooble::s_httpReferrerWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "Suppress HTTP Referrer Exceptions"));
  dooble::s_javaScriptExceptionsWindow = new dexceptionswindow
    (new dexceptionsmodel("javascriptexceptions"));
  dooble::s_javaScriptExceptionsWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "JavaScript Exceptions"));
  dooble::s_imageBlockWindow = new dexceptionswindow
    (new dexceptionsmodel("autoloadedimagesexceptions"));
  dooble::s_imageBlockWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "Automatically-Loaded Images Exceptions"));
  dooble::s_cacheExceptionsWindow = new dexceptionswindow
    (new dexceptionsmodel("cacheexceptions"));
  dooble::s_cacheExceptionsWindow->setWindowTitle
    (QObject::tr("Dooble Web Browser: "
		 "Cache Exceptions"));
  dooble::s_networkCache = new dnetworkcache();
  dfilemanager::treeModel = new QFileSystemModel();

  /*
  ** Please create dfilemanager::tableModel before using
  ** dooble::s_settingsWindow.
  */

  dfilemanager::tableModel = new dfilesystemmodel();
  dfilemanager::treeModel->setReadOnly(false);
  dfilemanager::tableModel->setReadOnly(false);
  dfilemanager::treeModel->setFilter(QDir::Drives | QDir::AllDirs |
				     QDir::System |
				     QDir::NoDotAndDotDot);
  dfilemanager::treeModel->setRootPath(QDir::rootPath());
  dfilemanager::tableModel->setFilter(QDir::AllEntries |
                                      QDir::System | QDir::NoDotAndDotDot);
  dfilemanager::tableModel->setRootPath(QDir::rootPath());

  if(dooble::s_settings.value("mainWindow/showHiddenFiles", true).toBool())
    {
      dfilemanager::treeModel->setFilter
	(dfilemanager::treeModel->filter() | QDir::Hidden);
      dfilemanager::tableModel->setFilter
	(dfilemanager::tableModel->filter() | QDir::Hidden);
    }

  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "applications");

    db.setDatabaseName(dooble::s_homePath +
		       QDir::separator() + "applications.db");

    if(db.open())
      {
	QSqlQuery query(db);

	if(query.exec("SELECT file_suffix, action FROM applications"))
	  while(query.next())
	    {
	      QString action("");
	      QString suffix(query.value(0).toString());

	      if(query.isNull(1))
		action = "prompt";
	      else
		{
		  action = query.value(1).toString();

		  QFileInfo fileInfo(action);

		  if(!fileInfo.isExecutable() || !fileInfo.isReadable())
		    {
		      action = "prompt";

		      /*
		      ** Correct damaged entries.
		      */

		      QSqlQuery updateQuery(db);

		      updateQuery.exec("PRAGMA synchronous = OFF");
		      updateQuery.prepare("UPDATE applications "
					  "SET action = NULL "
					  "WHERE file_suffix = ?");
		      updateQuery.bindValue(0, query.value(0));
		      updateQuery.exec();
		    }
		}

	      dooble::s_applicationsActions[suffix] = action;
	    }
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("applications");
  dooble::s_historyWindow = new dhistory();
  dooble::s_downloadWindow = new ddownloadwindow();
  dooble::s_settingsWindow = new dsettings();
  dooble::s_settingsWindow->slotPopulateApplications
    (dooble::s_applicationsActions);
  QObject::connect
    (dfilemanager::tableModel,
     SIGNAL(suffixesAdded(const QMap<QString, QString> &)),
     dooble::s_settingsWindow,
     SLOT(slotPopulateApplications(const QMap<QString, QString> &)));
  QObject::connect(dfilemanager::tableModel,
		   SIGNAL(suffixUpdated(const QString &, const QString &)),
		   dooble::s_settingsWindow,
		   SLOT(slotUpdateApplication(const QString &,
					      const QString &)));
  dooble::s_bookmarksPopup = new dbookmarkspopup(); /*
						    ** The object
						    ** dooble::
						    ** s_bookmarksPopup
						    ** must be created
						    ** before dooble::
						    ** s_bookmarksWindow.
						    */
  dooble::s_bookmarksPopupMenu = new QMenu(0);

  QWidgetAction *action = new QWidgetAction(0);

  action->setDefaultWidget(dooble::s_bookmarksPopup);
  dooble::s_bookmarksPopupMenu->addAction(action);
  QObject::connect(dooble::s_bookmarksPopup,
		   SIGNAL(closed(void)),
		   dooble::s_bookmarksPopupMenu,
		   SLOT(close(void)));
  dooble::s_bookmarksWindow = new dbookmarkswindow();
  dooble::s_cookies = new dcookies(); /*
				      ** The object dooble::s_cookies
				      ** must be created after
				      ** dooble::s_cookiesBlockWindow.
				      */
  dooble::s_cookieWindow = new dcookiewindow();
  dooble::s_clearContainersWindow = new dclearcontainers();
  QObject::connect(dooble::s_cookies,
		   SIGNAL(changed(void)),
		   dooble::s_cookieWindow,
		   SLOT(slotCookiesChanged(void)));
  QObject::connect(dooble::s_cookies,
		   SIGNAL(domainsRemoved(const QStringList &)),
		   dooble::s_cookieWindow,
		   SLOT(slotDomainsRemoved(const QStringList &)));
  QObject::connect(dooble::s_settingsWindow,
		   SIGNAL(cookieTimerChanged(void)),
		   dooble::s_cookies,
		   SLOT(slotCookieTimerChanged(void)));
  QObject::connect(dooble::s_historyWindow,
		   SIGNAL(bookmark(const QUrl &,
				   const QIcon &,
				   const QString &,
				   const QString &,
				   const QDateTime &,
				   const QDateTime &)),
		   dooble::s_bookmarksWindow,
		   SLOT(slotAddBookmark(const QUrl &,
					const QIcon &,
					const QString &,
					const QString &,
					const QDateTime &,
					const QDateTime &)));
  QObject::connect(dooble::s_bookmarksPopup,
		   SIGNAL(changed(void)),
		   dooble::s_bookmarksWindow,
		   SLOT(slotRefresh(void)));

  QUrl url;
  QLocale locale;
  QString urlText(dooble::s_settings.value("settingsWindow/homeUrl", "").
		  toString());

  url = QUrl::fromUserInput(urlText);

  if(argc > 1)
    {
      urls.append(url.toString(QUrl::StripTrailingSlash));
      argumentsHash["urls"] = urls;
      Q_UNUSED(new dooble(argumentsHash, 0));
    }
  else
    {
      Q_UNUSED(new dooble(url.toString(QUrl::StripTrailingSlash), 0));
    }

  /*
  ** If the last Dooble window is closed, Dooble will exit.
  ** So, it's not necessary to connect QApplication's
  ** lastWindowClosed() signal.
  */

  int rc = qapp.exec();

  dmisc::purgeFavicons();
  dmisc::destroyCrypt();
  return rc;
}

void dooble::init(const bool isJavaScriptWindow)
{
  /*
  ** This method is used whenever a new Dooble window
  ** is about to be created.
  */

  setUrlHandler(this);
  m_isJavaScriptWindow = isJavaScriptWindow;
  showFindFrame = false;
  s_instances += 1;
  m_id = QDateTime::currentMSecsSinceEpoch() + s_instances;
  ui.setupUi(this);
  ui.historyFrame->setLayout(new QHBoxLayout(ui.historyFrame));
  ui.historyFrame->layout()->setContentsMargins(1, 0, 1, 0);
  m_historySideBar = new dhistorysidebar(this);
  ui.historyFrame->layout()->addWidget(m_historySideBar);
  ui.actionShow_HistorySideBar->setChecked
    (s_settings.value("mainWindow/showHistorySideBar", false).
     toBool());
  ui.actionOffline->setChecked
    (s_settings.value("mainWindow/offlineMode", false).toBool());
  ui.historyFrame->setVisible(false);
  ui.actionZoom_Text_Only->setChecked
    (s_settings.value("mainWindow/zoomTextOnly", false).toBool());
#ifdef Q_WS_MAC
  setAttribute(Qt::WA_MacMetalStyle, true);

  /*
  ** Fixes shuffling.
  */

  statusBar()->setSizeGripEnabled(false);
#endif
  ui.urlFrame->setParent(0);
  ui.toolBar->setVisible(false);
  ui.toolBar->addWidget(ui.urlFrame);
  ui.toolBar->setVisible(true);
  ui.favoritesFrame->setParent(0);
  ui.favoritesToolBar->setVisible(false);
  ui.favoritesToolBar->addWidget(ui.favoritesFrame);
  ui.favoritesToolBar->setVisible(true);
#ifdef Q_WS_MAC
  ui.actionFull_Screen_Mode->setShortcut
    (QKeySequence(Qt::ControlModifier + Qt::Key_F11));
#endif
  ui.marker->setVisible(false);
  ui.restoreToolButton->setVisible(false);
  sbWidget = new QWidget(this);
  m_desktopWidget = 0;
  sb.setupUi(sbWidget);
  sb.progressBar->setValue(0);
  sb.progressBar->setVisible(false);
  sb.statusLabel->setVisible(true);
  sb.exceptionsToolButton->setVisible(false);
  sb.errorLogToolButton->setVisible(false);
#ifdef Q_WS_MAC
  sbWidget->layout()->setContentsMargins(0, 5, 15, 5);
#endif
  ui.findFrame->setVisible(false);
  ui.backToolButton->setMenu(new QMenu(this));
  connect(ui.actionOffline,
	  SIGNAL(triggered(bool)),
	  this,
	  SLOT(slotOffline(bool)));
  connect(ui.backToolButton->menu(),
	  SIGNAL(aboutToShow(void)),
	  this,
	  SLOT(slotAboutToShowBackForwardMenu(void)));
  connect(ui.bookmarksMenu,
	  SIGNAL(aboutToShow(void)),
	  this,
	  SLOT(slotAboutToShowBookmarksMenu(void)));
  ui.forwardToolButton->setMenu(new QMenu(this));
  connect(ui.forwardToolButton->menu(),
	  SIGNAL(aboutToShow(void)),
	  this,
	  SLOT(slotAboutToShowBackForwardMenu(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_historyWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_downloadWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_cookieWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_bookmarksPopup,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_bookmarksWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_errorLog,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(passphraseWasAuthenticated(const bool)),
	  s_settingsWindow,
	  SLOT(slotPassphraseWasAuthenticated(const bool)));
  connect(s_settingsWindow,
	  SIGNAL(iconsChanged(void)),
	  this,
	  SLOT(slotSetIcons(void)));
  connect(s_errorLog,
	  SIGNAL(iconsChanged(void)),
	  this,
	  SLOT(slotSetIcons(void)));
  connect(s_settingsWindow,
	  SIGNAL(showTabBar(const bool)),
	  this,
	  SLOT(slotSetTabBarVisible(const bool)));
  connect(s_settingsWindow,
	  SIGNAL(settingsReset(void)),
	  this,
	  SLOT(slotQuitAndRestart(void)));
  connect(s_settingsWindow,
	  SIGNAL(showIpAddress(const bool)),
	  this,
	  SLOT(slotShowIpAddress(const bool)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  ui.searchLineEdit,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  ui.locationLineEdit,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  ui.tabWidget,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  m_historySideBar,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_dntWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_popupsWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_adBlockWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_cookieWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_httpRedirectWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_httpReferrerWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_javaScriptExceptionsWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_imageBlockWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_cacheExceptionsWindow,
	  SLOT(slotSetIcons(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  s_clearContainersWindow,
	  SLOT(slotSetIcons(void)));

  /*
  ** Invoke the method when control has returned to the main
  ** thread.
  */

  connect(this,
	  SIGNAL(bookmarkUpdated(void)),
	  s_bookmarksWindow,
	  SLOT(slotRefresh(void)),
	  Qt::QueuedConnection);
  connect(s_networkCache,
	  SIGNAL(percentRead(const int)),
	  this,
	  SLOT(slotPercentReadFromCache(const int)));
  connect(s_networkCache,
	  SIGNAL(percentWritten(const int)),
	  this,
	  SLOT(slotPercentWrittenToDisk(const int)));
  connect(ui.splitter,
	  SIGNAL(splitterMoved(int, int)),
	  this,
	  SLOT(slotLocationSplitterMoved(int, int)));
  connect(ui.historyAndTabSplitter,
	  SIGNAL(splitterMoved(int, int)),
	  this,
	  SLOT(slotHistoryTabSplitterMoved(int, int)));
  connect(ui.locationLineEdit,
	  SIGNAL(loadPage(const QUrl &)),
	  this,
	  SLOT(slotLoadPage(const QUrl &)));
  connect(s_downloadWindow, SIGNAL(saveUrl(const QUrl &, const int)), this,
	  SLOT(slotSaveUrl(const QUrl &, const int)));
  connect(m_historySideBar, SIGNAL(open(const QUrl &)),
	  this, SLOT(slotLoadPage(const QUrl &)));
  connect(m_historySideBar, SIGNAL(createTab(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewTab(const QUrl &)));
  connect(m_historySideBar, SIGNAL(openInNewWindow(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewWindow(const QUrl &)));
  connect(m_historySideBar, SIGNAL(closed(void)),
	  this, SLOT(slotHistorySideBarClosed(void)));
  connect(ui.tabWidget, SIGNAL(currentChanged(int)), this,
	  SLOT(slotTabSelected(int)));
  connect(ui.actionNew_Tab, SIGNAL(triggered(void)), this,
	  SLOT(slotNewTab(void)));
  connect(ui.tabWidget, SIGNAL(createTab(void)), this,
	  SLOT(slotNewTab(void)));
  connect(ui.tabWidget, SIGNAL(openInNewWindow(const int)), this,
	  SLOT(slotOpenPageInNewWindow(const int)));
  connect(ui.actionNew_Window, SIGNAL(triggered(void)), this,
	  SLOT(slotNewWindow(void)));
  connect(ui.actionOpen_URL, SIGNAL(triggered(void)), this,
	  SLOT(slotOpenUrl(void)));
  connect(ui.actionClose_Tab, SIGNAL(triggered(void)), this,
	  SLOT(slotCloseTab(void)));
  connect(ui.tabWidget, SIGNAL(closeTab(const int)), this,
	  SLOT(slotCloseTab(const int)));
  connect(ui.tabWidget, SIGNAL(tabMoved(int, int)), this,
	  SLOT(slotTabMoved(int, int)));
  connect(ui.actionClose_Window, SIGNAL(triggered(void)), this,
	  SLOT(slotClose(void)));
  connect(ui.actionQuit, SIGNAL(triggered(void)), this,
	  SLOT(slotQuit(void)));
  connect(ui.locationLineEdit, SIGNAL(textEdited(const QString &)),
	  this, SLOT(slotTextChanged(const QString &)));
  connect(ui.locationLineEdit, SIGNAL(returnPressed(void)), this,
	  SLOT(slotLoadPage(void)));
  connect(ui.locationLineEdit, SIGNAL(selectionChanged(void)),
	  this, SLOT(slotSelectionChanged(void)));
  connect(ui.locationLineEdit, SIGNAL(openLinkInNewTab(const QUrl &)), this,
	  SLOT(slotOpenLinkInNewTab(const QUrl &)));
  connect(ui.locationLineEdit, SIGNAL(resetUrl(void)), this,
	  SLOT(slotResetUrl(void)));
  connect(ui.locationLineEdit, SIGNAL(bookmark(void)),
	  this, SLOT(slotBookmark(void)));
  connect(ui.backToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotBack(void)));
  connect(ui.forwardToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotForward(void)));
  connect(ui.reloadToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotReload(void)));
  connect(ui.actionReload, SIGNAL(triggered(void)), this,
	  SLOT(slotReload(void)));
  connect(ui.stopToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotStop(void)));
  connect(ui.actionStop, SIGNAL(triggered(void)), this,
	  SLOT(slotStop(void)));
  connect(ui.homeToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotGoHome(void)));
  connect(ui.searchLineEdit, SIGNAL(returnPressed(void)), this,
	  SLOT(slotSearch(void)));
  connect(ui.searchLineEdit->findButton(), SIGNAL(clicked(void)), this,
	  SLOT(slotSearch(void)));
  connect(ui.searchLineEdit, SIGNAL(selectionChanged(void)),
	  this, SLOT(slotSelectionChanged(void)));
  connect(ui.actionAbout_Dooble, SIGNAL(triggered(void)), this,
	  SLOT(slotAbout(void)));
  connect(ui.actionSave_Page, SIGNAL(triggered(void)), this,
	  SLOT(slotSavePage(void)));
  connect(ui.actionDownloads, SIGNAL(triggered(void)), this,
	  SLOT(slotDisplayDownloadWindow(void)));
  connect(ui.actionFind, SIGNAL(triggered(void)), this,
	  SLOT(slotShowFind(void)));
  connect(ui.actionPrint, SIGNAL(triggered(void)), this,
	  SLOT(slotPrint(void)));
  connect(ui.actionPrint_Preview, SIGNAL(triggered(void)), this,
	  SLOT(slotPrintPreview(void)));
  connect(ui.actionPage_Source, SIGNAL(triggered(void)), this,
	  SLOT(slotShowPageSource(void)));
  connect(ui.actionSettings, SIGNAL(triggered(void)), this,
	  SLOT(slotShowSettingsWindow(void)));
  connect(ui.hideFindToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotHideFind(void)));
  connect(ui.findLineEdit, SIGNAL(returnPressed(void)), this,
	  SLOT(slotFind(void)));
  connect(ui.findLineEdit, SIGNAL(textEdited(const QString &)), this,
	  SLOT(slotFind(void)));
  connect(ui.findLineEdit, SIGNAL(returnPressed(void)), this,
	  SLOT(slotFindNext(void)));
  connect(ui.findLineEdit, SIGNAL(textEdited(const QString &)), this,
	  SLOT(slotFindNext(void)));
  connect(ui.highlightAllCheckBox, SIGNAL(clicked(bool)), this,
	  SLOT(slotFind(void)));
  connect(ui.nextToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotFindNext(void)));
  connect(ui.previousToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotFindPrevious(void)));
  connect(ui.desktopToolButton, SIGNAL(clicked(void)), this,
	  SLOT(slotShowDesktopTab(void)));
  connect(ui.actionStatusbar, SIGNAL(triggered(bool)), this,
	  SLOT(slotStatusBarDisplay(bool)));
  connect(ui.actionShow_Hidden_Files, SIGNAL(triggered(bool)), this,
	  SLOT(slotShowHiddenFiles(bool)));
  connect(ui.actionOpen_Directory, SIGNAL(triggered(void)), this,
	  SLOT(slotOpenDirectory(void)));
  connect(ui.actionZoom_In, SIGNAL(triggered(void)), this,
	  SLOT(slotViewZoomIn(void)));
  connect(ui.actionZoom_Out, SIGNAL(triggered(void)), this,
	  SLOT(slotViewZoomOut(void)));
  connect(ui.actionReset_Zoom, SIGNAL(triggered(void)), this,
	  SLOT(slotViewResetZoom(void)));
  connect(ui.actionZoom_Text_Only, SIGNAL(toggled(bool)), this,
	  SLOT(slotViewZoomTextOnly(bool)));
  connect(ui.actionMy_Retrieved_Files, SIGNAL(triggered(void)), this,
	  SLOT(slotOpenMyRetrievedFiles(void)));
  connect(ui.actionP2P_Email, SIGNAL(triggered(void)), this,
	  SLOT(slotOpenP2PEmail(void)));
  connect(ui.action_IRC_Channel, SIGNAL(triggered(void)), this,
	  SLOT(slotOpenIrcChannel(void)));
  connect(ui.actionCopy, SIGNAL(triggered(void)), this,
	  SLOT(slotCopy(void)));
  connect(ui.actionPaste, SIGNAL(triggered(void)), this,
	  SLOT(slotPaste(void)));
  connect(ui.actionSelect_All_Content, SIGNAL(triggered(void)), this,
	  SLOT(slotSelectAllContent(void)));
  connect(ui.actionFull_Screen_Mode, SIGNAL(triggered(void)), this,
	  SLOT(slotFullScreenMode(void)));
  connect(ui.actionApplication_Cookies, SIGNAL(triggered(void)), this,
	  SLOT(slotShowApplicationCookies(void)));
  connect(ui.marker, SIGNAL(markerEntered(void)), this,
	  SLOT(slotMarkerEntered(void)));
  connect(ui.restoreToolButton, SIGNAL(clicked(void)),
	  this, SLOT(slotFullScreenMode(void)));
  connect(ui.actionShow_FavoritesToolBar, SIGNAL(toggled(bool)),
	  this, SLOT(slotShowFavoritesToolBar(bool)));
  connect(ui.actionShow_HistorySideBar, SIGNAL(toggled(bool)),
	  this, SLOT(slotShowHistorySideBar(bool)));
  connect(ui.action_Authenticate, SIGNAL(triggered(void)),
	  this, SLOT(slotAuthenticate(void)));
  connect(ui.actionRefreshPlugins, SIGNAL(triggered()),
          this, SLOT(slotRefreshPlugins()));
  connect(ui.menu_Plugins, SIGNAL (triggered(QAction*)),
          this, SLOT(slotPluginAction(QAction*)));
  connect(ui.tabWidget, SIGNAL(urlsReceivedViaDrop(const QList<QUrl> &)),
	  this, SLOT(slotOpenUrlsFromDrop(const QList<QUrl> &)));
  connect(ui.tabWidget, SIGNAL(openLinkInNewTab(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewTab(const QUrl &)));
  connect(ui.tabWidget, SIGNAL(bookmark(const int)),
	  this, SLOT(slotBookmark(const int)));
  connect(ui.tabWidget, SIGNAL(reloadTab(const int)),
	  this, SLOT(slotReloadTab(const int)));
  connect(ui.action_Cookies, SIGNAL(triggered(void)),
	  s_cookiesBlockWindow, SLOT(slotShow(void)));
  connect(ui.action_HTTP_Redirect, SIGNAL(triggered(void)),
	  s_httpRedirectWindow, SLOT(slotShow(void)));
  connect(ui.action_HTTP_Referrer, SIGNAL(triggered(void)),
	  s_httpReferrerWindow, SLOT(slotShow(void)));
  connect(ui.action_JavaScript, SIGNAL(triggered(void)),
	  s_javaScriptExceptionsWindow, SLOT(slotShow(void)));
  connect(ui.action_JavaScript_Popups, SIGNAL(triggered(void)),
	  s_popupsWindow, SLOT(slotShow(void)));
  connect(ui.action_Third_Party_Frame_Content, SIGNAL(triggered(void)),
	  s_adBlockWindow, SLOT(slotShow(void)));
  connect(ui.action_DNT, SIGNAL(triggered(void)),
	  s_dntWindow, SLOT(slotShow(void)));
  connect(ui.action_Automatically_Loaded_Images, SIGNAL(triggered(void)),
	  s_imageBlockWindow, SLOT(slotShow(void)));
  connect(ui.action_Cache, SIGNAL(triggered(void)),
	  s_cacheExceptionsWindow, SLOT(slotShow(void)));
  connect(ui.actionError_Log, SIGNAL(triggered(void)),
	  s_errorLog, SLOT(slotShow(void)));
  connect(ui.actionError_Log, SIGNAL(triggered(void)),
	  sb.errorLogToolButton, SLOT(hide(void)));
  connect(ui.favoritesToolBar,
	  SIGNAL(visibilityChanged(bool)),
	  ui.actionShow_FavoritesToolBar,
	  SLOT(setChecked(bool)));
  connect(s_cookies,
	  SIGNAL(exceptionRaised(dexceptionswindow *,
				 const QUrl &)),
	  this,
	  SLOT(slotExceptionRaised(dexceptionswindow *,
				   const QUrl &)));
  connect(s_bookmarksPopup,
	  SIGNAL(changed(void)),
	  this,
	  SLOT(slotBookmarksChanged(void)));
  connect(s_bookmarksWindow,
	  SIGNAL(changed(void)),
	  this,
	  SLOT(slotBookmarksChanged(void)));
  connect(s_errorLog,
	  SIGNAL(errorLogged(void)),
	  this,
	  SLOT(slotErrorLogged(void)));
  connect(sb.errorLogToolButton,
	  SIGNAL(clicked(void)),
	  s_errorLog,
	  SLOT(slotShow(void)));
  connect(sb.errorLogToolButton,
	  SIGNAL(clicked(void)),
	  sb.errorLogToolButton,
	  SLOT(hide(void)));
  connect(ui.action_Clear_Containers,
	  SIGNAL(triggered(void)),
	  this,
	  SLOT(slotClearContainers(void)));
  ui.viewMenu->insertSeparator(ui.zoomMenu->menuAction());

  if(ui.historyMenu->actions().isEmpty())
    {
      QAction *action = 0;
      QSettings settings
	(s_settings.value("iconSet").toString(), QSettings::IniFormat);

      action = ui.historyMenu->addAction
	(QIcon(settings.value("mainWindow/historyMenu").toString()),
	 tr("&Clear History"));
      action->setEnabled(false);
      connect(action, SIGNAL(triggered(void)), this,
	      SLOT(slotClearHistory(void)));
      action = ui.historyMenu->addAction
	(QIcon(settings.value("mainWindow/viewHistory").toString()),
	 tr("Show &History"));
      action->setShortcut(QKeySequence(Qt::ControlModifier + Qt::Key_H));
      action->setEnabled(true);
      connect(action, SIGNAL(triggered(void)), this,
	      SLOT(slotShowHistory(void)));
      ui.historyMenu->addSeparator();
      action = ui.historyMenu->addAction("&Recently-Closed Tabs");
      action->setEnabled(false);
    }

  ui.actionPrint->setEnabled(false);
  ui.actionPrint_Preview->setEnabled(false);
  ui.actionOpen_URL->setEnabled(false);
  ui.actionShow_Hidden_Files->setEnabled(false);
  ui.actionSave_Page->setEnabled(false);

  for(int i = 0; i < ui.tabWidget->count(); i++)
    if(qobject_cast<ddesktopwidget *> (ui.tabWidget->widget(i)))
      {
	ui.desktopToolButton->setEnabled(false);
	break;
      }

  /*
  ** According to Qt documentation, resize() followed by move() should
  ** be used to restore a window's geometry. Actual results may vary.
  */

  /*
  ** I don't know what to do about JavaScript windows that do not resize
  ** themselves.
  */

  if(s_settings.contains("mainWindow/geometry"))
    {
      if(!m_isJavaScriptWindow)
	{
	  if(dmisc::isGnome())
	    setGeometry(s_settings.
			value("mainWindow/geometry",
			      QRect(100, 100, 800, 600)).toRect());
	  else
	    {
	      QByteArray g(s_settings.value("mainWindow/geometry").
			   toByteArray());

	      if(!restoreGeometry(g))
		setGeometry(100, 100, 800, 600);
	    }
	}
      else
	{
	  if(m_parentWindow)
	    {
	      if(m_parentWindow->geometry().isValid())
		setGeometry
		  (dmisc::balancedGeometry(QRect(m_parentWindow->
						 geometry().x(),
						 m_parentWindow->
						 geometry().y(),
						 800, 600), this));
	      else
		setGeometry(dmisc::balancedGeometry(QRect(100, 100, 800, 600),
						    this));
	    }
	  else
	    setGeometry(dmisc::balancedGeometry(QRect(100, 100, 800, 600),
						this));
	}
    }
  else if(QApplication::desktop()->screenGeometry().isValid())
    {
      if(!m_isJavaScriptWindow)
	setGeometry
	  (dmisc::balancedGeometry(QRect(100, 100, 800, 600), this));
      else if(m_parentWindow)
	{
	  if(m_parentWindow->geometry().isValid())
	    setGeometry(dmisc::balancedGeometry(QRect(m_parentWindow->
						      geometry().x(),
						      m_parentWindow->
						      geometry().y(),
						      800, 600), this));
	  else
	    setGeometry(dmisc::balancedGeometry(QRect(100, 100, 800, 600),
						this));
	}
      else
	/*
	** Instead of sizeHint(), use a fixed value. sizeHint() may
	** return an invalid size.
	*/

	setGeometry(dmisc::balancedGeometry(QRect(100, 100, 800, 600),
					    this));
    }
  else if(m_parentWindow)
    {
      if(m_parentWindow->geometry().isValid())
	setGeometry(dmisc::balancedGeometry(QRect(m_parentWindow->
						  geometry().x(),
						  m_parentWindow->
						  geometry().y(),
						  800, 600), this));
      else
	setGeometry(dmisc::balancedGeometry(QRect(100, 100, 800, 600),
					    this));
    }
  else
    /*
    ** Please read the above comment.
    */

    setGeometry(dmisc::balancedGeometry(QRect(100, 100, 800, 600), this));

  if(s_settings.contains("mainWindow/state2"))
    restoreState(s_settings["mainWindow/state2"].toByteArray());

  if(s_settings.contains("mainWindow/splitterState"))
    ui.splitter->restoreState
      (s_settings.value("mainWindow/splitterState", "").toByteArray());

  if(s_settings.contains("mainWindow/historyTabSplitterState"))
    {
      if(!ui.historyAndTabSplitter->restoreState
	 (s_settings.value("mainWindow/historyTabSplitterState",
			   "").toByteArray()))
	{
	  ui.historyAndTabSplitter->setStretchFactor(0, 0);
	  ui.historyAndTabSplitter->setStretchFactor(1, 100);
	}
    }
  else
    {
      ui.historyAndTabSplitter->setStretchFactor(0, 0);
      ui.historyAndTabSplitter->setStretchFactor(1, 100);
    }

  statusBar()->addPermanentWidget(sbWidget, 100);
  statusBar()->setStyleSheet("QStatusBar::item {"
			     "border: none; "
			     "}");
  statusBar()->setMaximumHeight(sbWidget->height());
  statusBar()->setVisible
    (s_settings.value("mainWindow/statusbarDisplay", true).toBool());
  ui.actionStatusbar->setChecked(!statusBar()->isHidden());
  ui.actionShow_Hidden_Files->setChecked
    (s_settings.value("mainWindow/showHiddenFiles", true).toBool());
  ui.favoritesToolBar->setVisible
    (s_settings.value("mainWindow/showFavoritesToolBar", false).
     toBool());
  ui.actionShow_FavoritesToolBar->setChecked
    (s_settings.value("mainWindow/showFavoritesToolBar", false).
     toBool());
  ui.action_Authenticate->setEnabled
    (!dmisc::passphraseWasAuthenticated() && dmisc::passphraseWasPrepared());
  slotSetIcons();
#ifdef Q_WS_MAC
  foreach(QToolButton *toolButton, findChildren<QToolButton *> ())
    if(toolButton == ui.backToolButton ||
       toolButton == ui.forwardToolButton)
      toolButton->setStyleSheet
	("QToolButton {border: none; padding-right: 10px}"
	 "QToolButton::menu-button {border: none;}");
    else if(toolButton == ui.stopToolButton ||
	    toolButton == ui.reloadToolButton ||
	    toolButton == ui.homeToolButton ||
	    toolButton == ui.desktopToolButton ||
	    toolButton == ui.restoreToolButton ||
	    toolButton == sb.exceptionsToolButton ||
	    toolButton == sb.errorLogToolButton)
      toolButton->setStyleSheet
	("QToolButton {border: none;}"
	 "QToolButton::menu-button {border: none;}");

  int margins[4];

  centralWidget()->layout()->getContentsMargins(&margins[0], &margins[1],
						&margins[2], &margins[3]);
  centralWidget()->layout()->setContentsMargins(margins[0], 10,
						margins[2], margins[3]);
#endif
#ifdef Q_WS_X11
  setWindowRole("browser");
#endif
  slotRefreshPlugins();
}

dooble::dooble(const bool isJavaScriptWindow, dooble *d):QMainWindow()
{
  /*
  ** m_parentWindow is used for positioning JavaScript windows.
  */

  if(m_isJavaScriptWindow)
    m_parentWindow = d;
  else
    m_parentWindow = 0;

  init(isJavaScriptWindow);
  copyDooble(d);
  prepareMostVisited();

  if(!isJavaScriptWindow)
    if(s_settings.value("settingsWindow/displayDesktopCheckBox",
			false).toBool())
      slotShowDesktopTab(false);

  show();
  ui.historyFrame->setVisible
    (ui.actionShow_HistorySideBar->isChecked());
  update();

  if(!dmisc::passphraseWasPrepared() && s_instances <= 1)
    remindUserToSetPassphrase();
}

dooble::dooble(const QUrl &url, dooble *d):QMainWindow()
{
  m_parentWindow = 0;
  init(false);
  ui.tabWidget->setVisible(false);

  if(promptForPassphrase())
    {
      /*
      ** We're not going to populate the History model and window.
      */

      QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
      s_cookies->populate();
      s_cookieWindow->populate();
      s_downloadWindow->populate();
      s_bookmarksWindow->populate();
      s_popupsWindow->populate();
      s_adBlockWindow->populate();
      s_cookiesBlockWindow->populate();
      s_httpRedirectWindow->populate();
      s_httpReferrerWindow->populate();
      s_javaScriptExceptionsWindow->populate();
      s_dntWindow->populate();
      s_imageBlockWindow->populate();
      s_cacheExceptionsWindow->populate();
      s_networkCache->populate();
      QApplication::restoreOverrideCursor();
    }

  copyDooble(d);
  prepareMostVisited();
  ui.tabWidget->setVisible(true);
  newTab(url);

  if(s_settings.value("settingsWindow/displayDesktopCheckBox",
		      false).toBool())
    slotShowDesktopTab(false);

  show();
  ui.tabWidget->update();
  ui.historyFrame->setVisible
    (ui.actionShow_HistorySideBar->isChecked());
  reinstate();
  update();

  if(!dmisc::passphraseWasPrepared() && s_instances <= 1)
    remindUserToSetPassphrase();
}

dooble::dooble(dview *p, dooble *d):QMainWindow()
{
  /*
  ** This method is called when the user has decided to
  ** change a tab into a window.
  */

  m_parentWindow = 0;
  init(false);
  copyDooble(d);
  prepareMostVisited();
  newTab(p);

  if(s_settings.value("settingsWindow/displayDesktopCheckBox",
		      false).toBool())
    slotShowDesktopTab(false);

  show();
  ui.tabWidget->update();
  ui.historyFrame->setVisible(ui.actionShow_HistorySideBar->isChecked());
  update();
  ui.tabWidget->setTabText(ui.tabWidget->indexOf(p),
			   ui.tabWidget->tabText(ui.tabWidget->indexOf(p)));

  if(!dmisc::passphraseWasPrepared() && s_instances <= 1)
    remindUserToSetPassphrase();
}

dooble::dooble(const QByteArray &history, dooble *d):QMainWindow()
{
  m_parentWindow = 0;
  init(false);
  copyDooble(d);
  prepareMostVisited();
  newTab(history);

  if(s_settings.value("settingsWindow/displayDesktopCheckBox",
		      false).toBool())
    slotShowDesktopTab(false);

  show();
  ui.tabWidget->update();
  ui.historyFrame->setVisible
    (ui.actionShow_HistorySideBar->isChecked());
  update();

  if(!dmisc::passphraseWasPrepared() && s_instances <= 1)
    remindUserToSetPassphrase();
}

dooble::dooble(const QHash<QString, QVariant> &hash, dooble *d):QMainWindow()
{
  m_parentWindow = 0;
  init(false);
  ui.tabWidget->setVisible(false);

  if(promptForPassphrase())
    {
      /*
      ** We're not going to populate the History model and window.
      */

      QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
      s_cookies->populate();
      s_cookieWindow->populate();
      s_downloadWindow->populate();
      s_bookmarksWindow->populate();
      s_popupsWindow->populate();
      s_adBlockWindow->populate();
      s_cookiesBlockWindow->populate();
      s_httpRedirectWindow->populate();
      s_httpReferrerWindow->populate();
      s_javaScriptExceptionsWindow->populate();
      s_dntWindow->populate();
      s_imageBlockWindow->populate();
      s_cacheExceptionsWindow->populate();
      s_networkCache->populate();
      QApplication::restoreOverrideCursor();
    }

  copyDooble(d);
  prepareMostVisited();
  ui.tabWidget->setVisible(true);

  QStringList urls(hash["urls"].toStringList());

  while(!urls.isEmpty())
    {
      QUrl url(QUrl::fromUserInput(urls.takeFirst()));

      if(url.isValid())
	newTab(QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash)));
    }

  if(s_settings.value("settingsWindow/displayDesktopCheckBox",
		      false).toBool())
    slotShowDesktopTab(false);

  if(hash.contains("full-screen-mode"))
    slotFullScreenMode();

  show();
  ui.tabWidget->update();
  ui.historyFrame->setVisible
    (ui.actionShow_HistorySideBar->isChecked());
  reinstate();
  update();

  if(!dmisc::passphraseWasPrepared() && s_instances <= 1)
    remindUserToSetPassphrase();
}

dooble::~dooble()
{
  s_instances -= 1;
  m_closedTabs.clear();
}

void dooble::slotSetIcons(void)
{
  int i = 0;
  QSize size;
  QSettings settings(s_settings.value("iconSet").toString(),
		     QSettings::IniFormat);

  i = s_settings.value("settingsWindow/locationToolbarIconSizeIndex",
		       0).toInt();

  if(i == 0)
    size = QSize(24, 24);
  else
    size = QSize(32, 32);


  ui.backToolButton->setIconSize(size);
  ui.forwardToolButton->setIconSize(size);
  ui.reloadToolButton->setIconSize(size);
  ui.stopToolButton->setIconSize(size);
  ui.homeToolButton->setIconSize(size);
  ui.desktopToolButton->setIconSize(size);
  ui.backToolButton->setIcon
    (QIcon(settings.value("mainWindow/backToolButton").toString()));
  ui.forwardToolButton->setIcon
    (QIcon(settings.value("mainWindow/forwardToolButton").toString()));
  ui.reloadToolButton->setIcon
    (QIcon(settings.value("mainWindow/reloadToolButton").toString()));
  ui.stopToolButton->setIcon
    (QIcon(settings.value("mainWindow/stopToolButton").toString()));
  ui.homeToolButton->setIcon
    (QIcon(settings.value("mainWindow/homeToolButton").toString()));
  ui.hideFindToolButton->setIcon
    (QIcon(settings.value("mainWindow/hideFindToolButton").toString()));
  ui.actionNew_Tab->setIcon
    (QIcon(settings.value("mainWindow/actionNew_Tab").toString()));
  ui.actionNew_Window->setIcon
    (QIcon(settings.value("mainWindow/actionNew_Window").toString()));
  ui.actionOpen_URL->setIcon
    (QIcon(settings.value("mainWindow/actionOpen_URL").toString()));
  ui.actionClose_Tab->setIcon
    (QIcon(settings.value("mainWindow/actionClose_Tab").toString()));
  ui.actionClose_Window->setIcon
    (QIcon(settings.value("mainWindow/actionClose_Window").toString()));
  ui.actionSave_Page->setIcon
    (QIcon(settings.value("mainWindow/actionSave_Page").toString()));
  ui.actionPrint->setIcon
    (QIcon(settings.value("mainWindow/actionPrint").toString()));
  ui.actionPrint_Preview->setIcon
    (QIcon(settings.value("mainWindow/actionPrint_Preview").toString()));
  ui.actionQuit->setIcon
    (QIcon(settings.value("mainWindow/actionQuit").toString()));
  ui.actionFind->setIcon
    (QIcon(settings.value("mainWindow/actionFind").toString()));
  ui.actionReload->setIcon
    (QIcon(settings.value("mainWindow/actionReload").toString()));
  ui.actionStop->setIcon
    (QIcon(settings.value("mainWindow/actionStop").toString()));
  ui.actionPage_Source->setIcon
    (QIcon(settings.value("mainWindow/actionPage_Source").toString()));
  ui.actionDownloads->
    setIcon(QIcon(settings.value("mainWindow/actionDownloads").toString()));
  ui.actionSettings->setIcon
    (QIcon(settings.value("mainWindow/actionSettings").toString()));
  ui.actionAbout_Dooble->
    setIcon(QIcon(settings.value("mainWindow/actionAbout_Dooble").toString()));
  ui.desktopToolButton->
    setIcon(QIcon(settings.value("mainWindow/desktopToolButton").toString()));
  ui.actionOpen_Directory->setIcon
    (QIcon(settings.value("mainWindow/actionOpen_Directory").toString()));
  ui.actionMy_Retrieved_Files->setIcon
    (QIcon(settings.value("mainWindow/actionMy_Retrieved_Files").toString()));
  ui.restoreToolButton->setIcon
    (QIcon(settings.value("mainWindow/restoreToolButton").toString()));
  ui.actionCopy->setIcon
    (QIcon(settings.value("mainWindow/actionCopy").toString()));
  ui.actionPaste->setIcon
    (QIcon(settings.value("mainWindow/actionPaste").toString()));
  ui.actionSelect_All_Content->setIcon
    (QIcon(settings.value("mainWindow/actionSelect_All_Content").toString()));
  ui.zoomMenu->setIcon
    (QIcon(settings.value("mainWindow/zoomMenu").toString()));
  ui.actionFull_Screen_Mode->setIcon
    (QIcon(settings.value("mainWindow/actionFull_Screen_Mode").toString()));
  ui.nextToolButton->setIcon
    (QIcon(settings.value("mainWindow/nextToolButton").toString()));
  ui.previousToolButton->setIcon
    (QIcon(settings.value("mainWindow/previousToolButton").toString()));
  ui.actionApplication_Cookies->setIcon
    (QIcon(settings.value
	   ("mainWindow/actionApplication_Cookies").toString()));
  ui.actionP2P_Email->setIcon
    (QIcon(settings.value("mainWindow/actionP2P_Email").toString()));
  ui.action_IRC_Channel->setIcon
    (QIcon(settings.value("windowIcon").toString()));
  ui.action_Authenticate->setIcon
    (QIcon(settings.value("mainWindow/authenticate_Action").toString()));
  ui.actionRefreshPlugins->setIcon
    (QIcon(settings.value("mainWindow/actionRefreshPlugins").toString()));
  ui.actionError_Log->setIcon
    (QIcon(settings.value("mainWindow/actionError_Log").toString()));
  ui.action_Clear_Containers->setIcon
    (QIcon(settings.value("mainWindow/actionClear_Containers").toString()));
  sb.exceptionsToolButton->setIcon
    (QIcon(settings.value("mainWindow/exceptionToolButton").toString()));
  sb.errorLogToolButton->setIcon
    (QIcon(settings.value("mainWindow/errorLogToolButton").toString()));

  if(ui.historyMenu->actions().size() > 0)
    {
      ui.historyMenu->actions().at(0)->setIcon
	(QIcon(settings.value("mainWindow/historyMenu").toString()));

      if(ui.historyMenu->actions().size() >= 4 &&
	 ui.historyMenu->actions().at(3)->menu() &&
	 ui.historyMenu->actions().at(3)->menu()->actions().size() > 0)
	ui.historyMenu->actions().at(3)->menu()->actions().at(0)->setIcon
	  (QIcon(settings.value("mainWindow/historyMenu").toString()));
    }

  if(ui.historyMenu->actions().size() >= 2)
    ui.historyMenu->actions().at(1)->setIcon
      (QIcon(settings.value("mainWindow/viewHistory").toString()));

  if(ui.bookmarksMenu->actions().size() >= 3)
    {
      ui.bookmarksMenu->actions().at(0)->setIcon
	(QIcon(settings.value("mainWindow/actionBookmarks").toString()));
      ui.bookmarksMenu->actions().at(2)->setIcon
	(QIcon(settings.value("mainWindow/actionBookmarks").toString()));
    }

  QMessageBox *mb = findChild<QMessageBox *> ("about");

  if(mb)
    {
      for(int i = 0; i < mb->buttons().size(); i++)
	if(mb->buttonRole(mb->buttons().at(i)) == QMessageBox::AcceptRole)
	  mb->buttons().at(i)->setIcon
	    (QIcon(settings.value("okButtonIcon").toString()));

      mb->setWindowIcon
	(QIcon(settings.value("mainWindow/windowIcon").toString()));
    }

  /*
  ** The 0th tab is not necessarily the Desktop.
  */

  for(int i = 0; i < ui.tabWidget->count(); i++)
    if(qobject_cast<ddesktopwidget *> (ui.tabWidget->widget(i)))
      {
	ui.tabWidget->setTabIcon
	  (i, QIcon(settings.value("mainWindow/tabWidget").toString()));
	break;
      }

  setWindowIcon(QIcon(settings.value("mainWindow/windowIcon").toString()));
  emit iconsChanged();
}

void dooble::newTabInit(dview *p)
{
  if(!p)
    return;

  connect(p, SIGNAL(destroyed(QObject *)),
	  this, SLOT(slotObjectDestroyed(QObject *)));
  connect(p, SIGNAL(urlChanged(const QUrl &)), this,
	  SLOT(slotUrlChanged(const QUrl &)));
  connect(p, SIGNAL(titleChanged(const QString &)), this,
	  SLOT(slotTitleChanged(const QString &)));
  connect(p, SIGNAL(loadFinished(bool)), this,
	  SLOT(slotLoadFinished(bool)));
  connect(p, SIGNAL(loadProgress(int)), this,
	  SLOT(slotLoadProgress(int)));
  connect(p, SIGNAL(iconChanged(void)), this,
	  SLOT(slotIconChanged(void)));
  connect(p, SIGNAL(loadStarted(void)), this,
	  SLOT(slotLoadStarted(void)));
  connect(p, SIGNAL(openLinkInNewTab(const QUrl &)), this,
	  SLOT(slotOpenLinkInNewTab(const QUrl &)));
  connect(p, SIGNAL(openLinkInNewWindow(const QUrl &)), this,
	  SLOT(slotOpenLinkInNewWindow(const QUrl &)));
  connect(p, SIGNAL(copyLink(const QUrl &)), this,
	  SLOT(slotCopyLink(const QUrl &)));
  connect(p, SIGNAL(saveUrl(const QUrl &, const int)), this,
	  SLOT(slotSaveUrl(const QUrl &, const int)));
  connect(p, SIGNAL(saveFile(const QString &, const QUrl &, const int)),
	  this, SLOT(slotSaveFile(const QString &, const QUrl &, const int)));
  connect(p, SIGNAL(viewImage(const QUrl &)), this,
	  SLOT(slotLoadPage(const QUrl &)));
  connect(p, SIGNAL(ipAddressChanged(const QString &)), this,
	  SLOT(slotIpAddressChanged(const QString &)));
  connect(p, SIGNAL(printRequested(QWebFrame *)), this,
	  SLOT(slotPrintRequested(QWebFrame *)));
  connect(p->page(),
	  SIGNAL(linkHovered(const QString &, const QString &,
			     const QString &)),
	  this,
	  SLOT(slotLinkHovered(const QString &, const QString &,
			       const QString &)));
  connect(p->page()->networkAccessManager(),
	  SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)),
	  this,
	  SLOT(slotAuthenticationRequired(QNetworkReply *, QAuthenticator *)));
  connect(p->page()->networkAccessManager(),
	  SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &,
					     QAuthenticator *)),
	  this,
	  SLOT(slotProxyAuthenticationRequired(const QNetworkProxy &,
					       QAuthenticator *)));
  connect(p->page(), SIGNAL(windowCloseRequested(void)),
	  this, SLOT(slotCloseWindow(void)));
  connect(p->page(), SIGNAL(menuBarVisibilityChangeRequested(bool)),
	  this, SLOT(slotHideMenuBar(void)));
  connect(p->page(), SIGNAL(statusBarVisibilityChangeRequested(bool)),
	  this, SLOT(slotHideStatusBar(void)));
  connect(p->page(), SIGNAL(toolBarVisibilityChangeRequested(bool)),
	  this, SLOT(slotHideToolBar(void)));
  connect(p->page(), SIGNAL(printRequested(QWebFrame *)),
	  this, SLOT(slotPrintRequested(QWebFrame *)));
  connect(p->page(), SIGNAL(geometryChangeRequested(const QRect &)),
	  this, SLOT(slotGeometryChangeRequested(const QRect &)));
  connect(p->page(), SIGNAL(repaintRequested(const QRect &)),
	  this, SLOT(slotRepaintRequested(const QRect &)));
  connect(this,
	  SIGNAL(clearHistory(void)),
	  p,
	  SLOT(slotClearHistory(void)));
  connect(p, SIGNAL(viewEntered(void)), this,
	  SLOT(slotViewEntered(void)));
  connect(p, SIGNAL(selectionChanged(const QString &)),
	  this, SLOT(slotSelectionChanged(const QString &)));
  connect(p, SIGNAL(viewPageSource(void)),
	  this, SLOT(slotShowPageSource(void)));
  connect(p, SIGNAL(goBack(void)),
	  this, SLOT(slotBack(void)));
  connect(p, SIGNAL(goReload(void)),
	  this, SLOT(slotReload(void)));
  connect(p, SIGNAL(goForward(void)),
	  this, SLOT(slotForward(void)));
  connect(p->page(),
	  SIGNAL(exceptionRaised(dexceptionswindow *,
				 const QUrl &)),
	  this,
	  SLOT(slotExceptionRaised(dexceptionswindow *,
				   const QUrl &)));
  connect(p->page()->networkAccessManager(),
	  SIGNAL(exceptionRaised(dexceptionswindow *,
				 const QUrl &)),
	  this,
	  SLOT(slotExceptionRaised(dexceptionswindow *,
				   const QUrl &)));

  int index = 0;
  QString title(p->title());

  if(title.isEmpty())
    title = p->url().toString(QUrl::StripTrailingSlash);

  if(title.isEmpty())
    title = tr("(Untitled)");

  if(s_settings.value("settingsWindow/appendNewTabs", false).toBool())
    {
      index = ui.tabWidget->addTab(p, title.replace("&", "&&"));
      ui.tabWidget->setTabToolTip(index, title);
    }
  else
    {
      index = ui.tabWidget->currentIndex() + 1;
      index = ui.tabWidget->insertTab(index, p, title.replace("&", "&&"));
      ui.tabWidget->setTabToolTip(index, title);
    }

  ui.tabWidget->setTabButton(index);

  /*
  ** If p came from another window, its animation icon will
  ** appear distorted because of the new tab
  ** having a "selected" background color (stylesheet).
  */

  ui.tabWidget->animateIndex(index, !p->isLoaded(), p->webviewIcon());

  if(p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    sb.statusLabel->clear();

  ui.tabWidget->setTabsClosable(ui.tabWidget->count() > 1);

  if(ui.tabWidget->count() == 1)
    ui.tabWidget->setBarVisible
      (s_settings.value("settingsWindow/alwaysShowTabBar",
			true).toBool());
  else
    ui.tabWidget->setBarVisible(true);
}

dview *dooble::newTab(dview *p)
{
  if(p)
    {
      p->removeRestorationFiles();
      p->setParent(this);
      newTabInit(p);

      if(p->url().isEmpty() || !p->url().isValid())
	slotOpenUrl();

      QString title(p->title());

      if(title.isEmpty())
	title = p->url().toString(QUrl::StripTrailingSlash);

      title = dmisc::elidedTitleText(title);

      if(title.isEmpty())
	title = tr("(Untitled)");

      QAction *action = new QAction(p->icon(), title, this);

      action->setData(p->url());
      connect(action, SIGNAL(triggered(void)), this,
	      SLOT(slotLinkActionTriggered(void)));
      p->setTabAction(action);
      prepareTabsMenu();

      /*
      ** Record the restoration file after p has been assigned to
      ** this window.
      */

      p->recordRestorationHistory();
    }

  return p;
}

dview *dooble::newTab(const QUrl &url)
{
  dview *p = new dview(this, QByteArray());

  newTabInit(p);

  if(p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      ui.locationLineEdit->setText(url.toString(QUrl::StripTrailingSlash));

      if(url.isEmpty() || !url.isValid())
	slotOpenUrl();
    }

  QAction *action = new QAction(p->icon(), tr("(Untitled)"), this);

  action->setData(url);
  connect(action, SIGNAL(triggered(void)), this,
	  SLOT(slotLinkActionTriggered(void)));
  p->setTabAction(action);
  p->load(url);
  prepareTabsMenu();
  prepareWidgetsBasedOnView(p);
  return p;
}

dview *dooble::newTab(const QByteArray &history)
{
  QUrl url;
  QIcon icon;
  dview *p = new dview(this, history);

  newTabInit(p);

  if(!history.isEmpty())
    {
      QByteArray h(history);
      QDataStream in(&h, QIODevice::ReadOnly);

      in >> *p->page()->history();
      url = p->page()->history()->currentItem().url();
      icon = p->page()->history()->currentItem().icon();
    }

  if(p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      ui.locationLineEdit->setText(url.toString(QUrl::StripTrailingSlash));

      if(url.isEmpty() || !url.isValid())
	slotOpenUrl();
    }

  if(icon.isNull())
    icon = dmisc::iconForUrl(url);

  QAction *action = new QAction(icon, tr("(Untitled)"), this);

  action->setData(url);
  connect(action, SIGNAL(triggered(void)), this,
	  SLOT(slotLinkActionTriggered(void)));
  p->setTabAction(action);
  p->load(url);
  prepareTabsMenu();
  prepareWidgetsBasedOnView(p);
  return p;
}

void dooble::cleanupBeforeExit(void)
{
  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
  dmisc::removeRestorationFiles(s_id);
  s_historyWindow->deleteLater();
  s_bookmarksPopup->deleteLater();
  s_downloadWindow->deleteLater();
  s_bookmarksWindow->deleteLater();
  s_cookies->deleteLater();
  s_cookieWindow->deleteLater();
  s_historyModel->deleteLater();
  s_popupsWindow->deleteLater();
  s_adBlockWindow->deleteLater();
  s_cookiesBlockWindow->deleteLater();
  s_httpRedirectWindow->deleteLater();
  s_httpReferrerWindow->deleteLater();
  s_javaScriptExceptionsWindow->deleteLater();
  s_dntWindow->deleteLater();
  s_errorLog->deleteLater();
  s_imageBlockWindow->deleteLater();
  s_cacheExceptionsWindow->deleteLater();
  s_networkCache->deleteLater();
  s_clearContainersWindow->deleteLater();
  QApplication::restoreOverrideCursor();
}

void dooble::slotQuit(void)
{
  /*
  ** closeEvent() may call this slot.
  */

  if(sender() != 0)
    {
      if(!warnAboutDownloads())
	return;

      if(!warnAboutTabs(QApplication::instance()))
	return;
    }

  cleanupBeforeExit();
  saveSettings();
  QApplication::instance()->exit(0);
}

void dooble::slotQuitAndRestart(void)
{
  cleanupBeforeExit();
  saveSettings();
  QApplication::instance()->exit(0);
  QProcess::startDetached(QCoreApplication::applicationDirPath() +
			  QDir::separator() +
			  QCoreApplication::applicationName());
}

void dooble::saveSettings(void)
{
  if(!m_isJavaScriptWindow && !isFullScreen())
    {
      QSettings settings;

      settings.setValue("mainWindow/state2", saveState());

      if(dmisc::isGnome())
	settings.setValue("mainWindow/geometry", geometry());
      else
	settings.setValue("mainWindow/geometry", saveGeometry());

      settings.setValue
	("mainWindow/ftpManagerColumnsState1",
	 s_settings.value
	 ("mainWindow/ftpManagerColumnsState1").toByteArray());
      settings.setValue
	("mainWindow/fileManagerColumnsState1",
	 s_settings.value
	 ("mainWindow/fileManagerColumnsState1").toByteArray());
      s_settings["mainWindow/state2"] = saveState();

      if(dmisc::isGnome())
	s_settings["mainWindow/geometry"] = geometry();
      else
	s_settings["mainWindow/geometry"] = saveGeometry();
    }
}

void dooble::loadPage(const QUrl &url)
{
  if(url.isEmpty() || !url.isValid())
    {
      /*
      ** Prevent the user from loading an invalid URL.
      */

      slotOpenUrl();
      return;
    }

  QUrl l_url(dmisc::correctedUrlPath(url));
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      sb.statusLabel->clear();

      QString str(l_url.toString(QUrl::StripTrailingSlash).trimmed());

      ui.locationLineEdit->setBookmarkButtonEnabled(false);

      if(ui.bookmarksMenu->actions().size() > 0)
	ui.bookmarksMenu->actions().at(0)->setEnabled(false);

      ui.locationLineEdit->setIcon(dmisc::iconForUrl(QUrl()));
      ui.locationLineEdit->setText(str);

      if(p->tabAction())
	p->tabAction()->setIcon(dmisc::iconForUrl(QUrl()));

      p->load(l_url);
      ui.zoomMenu->setEnabled(!p->isDir() && !p->isFtp());
      ui.actionSelect_All_Content->setEnabled(!p->isDir() && !p->isFtp());
      ui.actionFind->setEnabled(!p->isDir() && !p->isFtp());

      if(p->isDir() || p->isFtp())
	ui.findFrame->setVisible(false);
      else
	ui.findFrame->setVisible(showFindFrame);
    }
  else
    {
      newTab(l_url);
      ui.tabWidget->update();
    }
}

void dooble::slotLoadPage(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p && p->isModified() && !warnAboutLeavingModifiedTab())
    return;

  QString str(ui.locationLineEdit->text().trimmed());

  loadPage(QUrl::fromEncoded(QUrl::fromUserInput(str).
			     toEncoded(QUrl::StripTrailingSlash)));

  if(ui.tabWidget->currentWidget())
    ui.tabWidget->currentWidget()->setFocus();
}

void dooble::slotLoadPage(const QUrl &url)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p && p->isModified() && !warnAboutLeavingModifiedTab())
    {
      if(sender() == ui.locationLineEdit)
	{
	  ui.locationLineEdit->setIcon(p->icon());
	  ui.locationLineEdit->setText
	    (p->url().toString(QUrl::StripTrailingSlash));
	}

      return;
    }

  loadPage(url);

  if(ui.tabWidget->currentWidget())
    ui.tabWidget->currentWidget()->setFocus();
}

void dooble::slotNewTab(void)
{
  QUrl url;
  dview *p = 0;
  QLocale locale;

  url = QUrl::fromUserInput(s_settings.value("settingsWindow/homeUrl",
					     "").toString());

  if(!url.isValid())
    url = QUrl();
  else
    url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));

  p = newTab(url);

  if(p)
    {
      ui.tabWidget->setCurrentWidget(p);
      ui.tabWidget->update();

      if(url.isEmpty() || !url.isValid())
	/*
	** p's url may be empty at this point.
	*/

	slotOpenUrl();
    }
}

void dooble::slotNewWindow(void)
{
  QUrl url;
  QLocale locale;

  url = QUrl::fromUserInput(s_settings.value("settingsWindow/homeUrl", "").
			    toString());

  if(!url.isValid())
    url = QUrl();
  else
    url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));

  if(s_settings.value("settingsWindow/openUserWindowsInNewProcesses",
		      false).toBool())
    launchDooble(url);
  else
    {
      Q_UNUSED(new dooble(url, this));
    }
}

void dooble::slotClose(void)
{
  close();
}

bool dooble::warnAboutTabs(QObject *object)
{
  /*
  ** Returns true if the warning was accepted or
  ** if the setting is disabled.
  */

  if(s_settings.value("settingsWindow/warnBeforeClosingModifiedTab", false).
     toBool())
    {
      bool ask = false;

      if(qobject_cast<QApplication *> (object))
	{
	  foreach(QWidget *widget, QApplication::allWidgets())
	    {
	      dview *p = qobject_cast<dview *> (widget);

	      if(p && p->isModified())
		{
		  ask = true;
		  break;
		}
	    }
	}
      else
	{
	  dooble *d = qobject_cast<dooble *> (object);

	  if(d)
	    for(int i = 0; i < d->ui.tabWidget->count(); i++)
	      {
		dview *p = qobject_cast<dview *> (d->ui.tabWidget->widget(i));

		if(p && p->isModified())
		  {
		    ask = true;
		    break;
		  }
	      }
	}

      if(ask)
	{
	  QMessageBox mb(this);

	  mb.setIcon(QMessageBox::Question);
	  mb.setWindowTitle(tr("Dooble Web Browser: Confirmation"));
	  mb.setWindowModality(Qt::WindowModal);
	  mb.setStandardButtons(QMessageBox::No | QMessageBox::Yes);

	  if(s_instances <= 1 || qobject_cast<QApplication *> (object))
	    mb.setText(tr("You have tabs with modified content. "
			  "Are you sure that you wish to exit?"));
	  else
	    mb.setText(tr("You have tabs with modified content. "
			  "Are you sure that you wish to close?"));

	  QSettings settings(s_settings.value("iconSet").toString(),
			     QSettings::IniFormat);

	  for(int i = 0; i < mb.buttons().size(); i++)
	    if(mb.buttonRole(mb.buttons().at(i)) == QMessageBox::YesRole)
	      {
		mb.buttons().at(i)->setIcon
		  (QIcon(settings.value("okButtonIcon").toString()));
		mb.buttons().at(i)->setIconSize(QSize(16, 16));
	      }
	    else
	      {
		mb.buttons().at(i)->setIcon
		  (QIcon(settings.value("cancelButtonIcon").toString()));
		mb.buttons().at(i)->setIconSize(QSize(16, 16));
	      }

	  mb.setWindowIcon
	    (QIcon(settings.value("mainWindow/windowIcon").toString()));

	  if(mb.exec() == QMessageBox::No)
	    return false;
	}
    }

  return true;
}

bool dooble::warnAboutDownloads(void)
{
  /*
  ** Returns true if the warning was accepted.
  */

  if(s_downloadWindow && s_downloadWindow->isActive())
    {
      QMessageBox mb(this);

      mb.setIcon(QMessageBox::Question);
      mb.setWindowTitle(tr("Dooble Web Browser: Confirmation"));
      mb.setWindowModality(Qt::WindowModal);
      mb.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
      mb.setText(tr("Terminating Dooble "
		    "will cause existing downloads to be interrupted. "
		    "Are you sure that you wish to continue?"));

      QSettings settings(s_settings.value("iconSet").toString(),
			 QSettings::IniFormat);

      for(int i = 0; i < mb.buttons().size(); i++)
	if(mb.buttonRole(mb.buttons().at(i)) == QMessageBox::YesRole)
	  {
	    mb.buttons().at(i)->setIcon
	      (QIcon(settings.value("okButtonIcon").toString()));
	    mb.buttons().at(i)->setIconSize(QSize(16, 16));
	  }
	else
	  {
	    mb.buttons().at(i)->setIcon
	      (QIcon(settings.value("cancelButtonIcon").toString()));
	    mb.buttons().at(i)->setIconSize(QSize(16, 16));
	  }

      mb.setWindowIcon
	(QIcon(settings.value("mainWindow/windowIcon").toString()));

      if(mb.exec() == QMessageBox::No)
	return false;
    }

  return true;
}

void dooble::closeEvent(QCloseEvent *event)
{
  if(s_instances <= 1 && !warnAboutDownloads())
    {
      if(event)
	event->ignore();

      return;
    }

  if(!warnAboutTabs(this))
    {
      if(event)
	event->ignore();

      return;
    }

  unsetUrlHandler();
  deleteLater();
  QMainWindow::closeEvent(event);

  if(s_instances <= 1)
    slotQuit();
}

void dooble::slotStop(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    p->stop();

  sb.progressBar->setValue(0);
  sb.progressBar->setVisible(false);
  ui.reloadStopWidget->setCurrentIndex(1);
  ui.stopToolButton->setEnabled(false);
  ui.actionStop->setEnabled(false);
}

void dooble::slotReload(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      if(p->isModified() && !warnAboutLeavingModifiedTab())
	return;

      p->reload();
    }
}

void dooble::slotBack(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      if(p->isModified() && !warnAboutLeavingModifiedTab())
	return;

      p->back();
      ui.forwardToolButton->setEnabled(p->canGoForward());
    }
}

void dooble::slotForward(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      if(p->isModified() && !warnAboutLeavingModifiedTab())
	return;

      p->forward();
      ui.backToolButton->setEnabled(p->canGoBack());
    }
}

void dooble::slotTabSelected(const int index)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->widget(index));

  if(p)
    {
      ui.homeToolButton->setEnabled(true);
      ui.searchLineEdit->setVisible(!m_isJavaScriptWindow);
      ui.locationLineEdit->setVisible(true);
      ui.reloadToolButton->setEnabled(true);
      ui.searchLineEdit->setEnabled(true);
      ui.editMenu->setEnabled(true);
      ui.viewMenu->setEnabled(true);
      ui.actionPrint->setEnabled(true);
      ui.actionPrint_Preview->setEnabled(true);
      ui.actionOpen_URL->setEnabled(true);
      ui.actionSave_Page->setEnabled(true);

      if(p->title().isEmpty())
	{
	  if(p->ipAddress().isEmpty() ||
	     !s_settings.value("settingsWindow/displayIpAddress",
			       false).toBool())
	    setWindowTitle(tr("Dooble Web Browser"));
	  else
	    setWindowTitle(QString(tr("Dooble Web Browser (%1)")).
			   arg(p->ipAddress()));
	}
      else
	{
	  if(p->ipAddress().isEmpty() ||
	     !s_settings.value("settingsWindow/displayIpAddress",
			       false).toBool())
	    setWindowTitle(p->title() + tr(" - Dooble Web Browser"));
	  else
            setWindowTitle(p->title() + QString(" (%1)").
			   arg(p->ipAddress()) + tr(" - Dooble Web Browser"));
	}

      /*
      ** Bound the progress bar's value. Some styles (GTK+) issue warnings
      ** if the value is outside of the acceptable range.
      */

      sb.progressBar->setValue(qBound(sb.progressBar->minimum(),
				      p->progress(),
				      sb.progressBar->maximum()));
      sb.progressBar->setVisible(!p->isLoaded());
      ui.reloadStopWidget->setCurrentIndex(p->isLoaded() ? 1 : 0);
      ui.stopToolButton->setEnabled(!p->isLoaded());
      ui.actionStop->setEnabled(!p->isLoaded());
      ui.backToolButton->setEnabled(p->canGoBack());
      ui.forwardToolButton->setEnabled(p->canGoForward());

      if(p->url().toString(QUrl::StripTrailingSlash) !=
	 p->webviewUrl().toString(QUrl::StripTrailingSlash))
	ui.locationLineEdit->setIcon(dmisc::iconForUrl(QUrl()));
      else
	ui.locationLineEdit->setIcon(p->icon());

      ui.locationLineEdit->setText(p->url().toString(QUrl::StripTrailingSlash));
      ui.locationLineEdit->setSecureColor(p->hasSecureConnection());

      bool isBookmarkWorthy = p->isBookmarkWorthy();

      ui.locationLineEdit->setBookmarkButtonEnabled(isBookmarkWorthy);
      ui.locationLineEdit->setBookmarkColor(p->isBookmarked());

      if(ui.bookmarksMenu->actions().size() > 0)
	ui.bookmarksMenu->actions().at(0)->setEnabled(isBookmarkWorthy);

      sb.statusLabel->setText(p->statusMessage());
      statusBar()->setVisible(ui.actionStatusbar->isChecked());
      prepareWidgetsBasedOnView(p);

      if(p->url().isEmpty() || !p->url().isValid() || p->isDir())
	slotOpenUrl();
      else
	p->setFocus();

      p->update();
    }
  else
    {
      setWindowTitle(tr("Dooble Web Browser"));
      ui.forwardToolButton->setEnabled(false);
      ui.backToolButton->setEnabled(false);
      ui.stopToolButton->setEnabled(false);
      ui.reloadToolButton->setEnabled(false);
      ui.homeToolButton->setEnabled(false);
      ui.searchLineEdit->setVisible(false);
      ui.locationLineEdit->setVisible(false);
      ui.editMenu->setEnabled(false);
      ui.viewMenu->setEnabled(false);
      ui.actionPrint->setEnabled(false);
      ui.actionPrint_Preview->setEnabled(false);
      ui.actionOpen_URL->setEnabled(false);
      ui.actionSave_Page->setEnabled(false);
      ui.locationLineEdit->setBookmarkButtonEnabled(false);
      ui.locationLineEdit->setBookmarkColor(false);

      if(ui.bookmarksMenu->actions().size() > 0)
	ui.bookmarksMenu->actions().at(0)->setEnabled(false);

      ui.searchLineEdit->clear();
      ui.findFrame->setVisible(false);
      sb.statusLabel->clear();
      sb.progressBar->setValue(0);
      sb.progressBar->setVisible(false);
      statusBar()->setVisible(false);

      QWidget *widget = ui.tabWidget->widget(index);

      if(widget)
	widget->setFocus();
    }
}

void dooble::slotTextChanged(const QString &text)
{
  Q_UNUSED(text);

  QIcon icon;
  QSettings settings
    (s_settings.value("iconSet").toString(), QSettings::IniFormat);

  icon = QIcon(settings.value("urlWidget/emptyIcon").toString());
  ui.locationLineEdit->setBookmarkButtonEnabled(false);
  ui.locationLineEdit->setIcon(icon);
  ui.locationLineEdit->setSecureColor(false);
  ui.locationLineEdit->setBookmarkColor(false);

  if(ui.bookmarksMenu->actions().size() > 0)
    ui.bookmarksMenu->actions().at(0)->setEnabled(false);
}

void dooble::slotUrlChanged(const QUrl &url)
{
  if(url.isEmpty() || !url.isValid())
    return;

  ui.locationLineEdit->addItem(url.toString(QUrl::StripTrailingSlash));

  dview *p = qobject_cast<dview *> (sender());

  if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      prepareWidgetsBasedOnView(p);
      ui.backToolButton->setEnabled(p->canGoBack());
      ui.forwardToolButton->setEnabled(p->canGoForward());

      bool isBookmarkWorthy = p->isBookmarkWorthy();

      ui.locationLineEdit->setBookmarkButtonEnabled(isBookmarkWorthy);
      ui.locationLineEdit->setBookmarkColor(p->isBookmarked());
      ui.locationLineEdit->setSecureColor(p->hasSecureConnection());

      if(ui.bookmarksMenu->actions().size() > 0)
	ui.bookmarksMenu->actions().at(0)->setEnabled(isBookmarkWorthy);
    }

  /*
  ** The URL widget's text should not be changed if the
  ** user has edited its contents. However, how will
  ** the widget reflect the current URL?
  */

  if(!ui.locationLineEdit->hasFocus() ||
     !ui.locationLineEdit->isModified())
    if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
      ui.locationLineEdit->setText(url.toString(QUrl::StripTrailingSlash));
}

void dooble::slotTitleChanged(const QString &titleArg)
{
  dview *p = qobject_cast<dview *> (sender());
  QString title(titleArg.trimmed());

  if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      if(title.isEmpty())
	{
	  if(p->ipAddress().isEmpty() ||
	     !s_settings.value("settingsWindow/displayIpAddress",
			       false).toBool())
	    setWindowTitle(tr("Dooble Web Browser"));
	  else
	    setWindowTitle(QString(tr("Dooble Web Browser (%1)")).
			   arg(p->ipAddress()));
	}
      else
	{
	  if(p->ipAddress().isEmpty() ||
	     !s_settings.value("settingsWindow/displayIpAddress",
			       false).toBool())
	    setWindowTitle(title + tr(" - Dooble Web Browser"));
	  else
	    setWindowTitle(title + QString(" (%1)").arg(p->ipAddress()) +
			   tr(" - Dooble Web Browser"));
	}
    }

  if(p)
    {
      title = p->title();

      if(title.isEmpty())
	/*
	** The tab's title will be set to the URL.
	*/

	title = p->url().toString(QUrl::StripTrailingSlash);

      if(title.isEmpty())
	title = tr("(Untitled)");

      ui.tabWidget->setTabText(ui.tabWidget->indexOf(p),
			       title.replace("&", "&&"));
      ui.tabWidget->setTabToolTip(ui.tabWidget->indexOf(p), title);
    }
}

void dooble::slotIconChanged(void)
{
  dview *p = qobject_cast<dview *> (sender());

  if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    ui.locationLineEdit->setIcon(p->icon());

  if(p)
    {
      ui.tabWidget->animateIndex
	(ui.tabWidget->indexOf(p), !p->isLoaded(), p->icon());

      int index = ui.locationLineEdit->findText
	(p->url().toString(QUrl::StripTrailingSlash));

      if(index > -1)
	ui.locationLineEdit->setItemIcon(index, p->icon());

      QList<QAction *> actions = ui.historyMenu->actions();

      if(actions.size() >= 6)
	for(int i = 5; i < actions.size(); i++)
	  if(actions.at(i)->data().toUrl().
	     toString(QUrl::StripTrailingSlash) ==
	     p->url().toString(QUrl::StripTrailingSlash))
	    {
	      actions.at(i)->setIcon(p->icon());
	      break;
	    }

      if(ui.favoritesToolBar->isVisible())
	foreach(QToolButton *toolButton,
		ui.favoritesToolButtonsFrame->findChildren<QToolButton *> ())
	  if(toolButton->property("url").toUrl().
	     toString(QUrl::StripTrailingSlash) ==
	     p->url().toString(QUrl::StripTrailingSlash))
	    {
	      toolButton->setIcon(p->icon());
	      break;
	    }
    }
}

void dooble::slotLoadProgress(int progress)
{
  dview *p = qobject_cast<dview *> (sender());

  if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      if(p->isDir() || p->isFtp())
	sb.progressBar->setMaximum(0);
      else
	sb.progressBar->setMaximum(100);

      sb.progressBar->setVisible(!p->isLoaded());
      ui.reloadStopWidget->setCurrentIndex(p->isLoaded() ? 1 : 0);
      ui.stopToolButton->setEnabled(!p->isLoaded());
      ui.actionStop->setEnabled(p->isLoaded());
      ui.backToolButton->setEnabled(p->canGoBack());
      ui.forwardToolButton->setEnabled(p->canGoForward());

      bool isBookmarkWorthy = p->isBookmarkWorthy();

      ui.locationLineEdit->setBookmarkButtonEnabled(isBookmarkWorthy);
      ui.locationLineEdit->setBookmarkColor(p->isBookmarked());

      if(ui.bookmarksMenu->actions().size() > 0)
	ui.bookmarksMenu->actions().at(0)->setEnabled(isBookmarkWorthy);

      sb.progressBar->setValue(qBound(sb.progressBar->minimum(),
				      progress,
				      sb.progressBar->maximum()));
      sb.statusLabel->setText(p->statusMessage());
    }
}

void dooble::slotLoadFinished(bool ok)
{
  dview *p = qobject_cast<dview *> (sender());

  if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      sb.progressBar->setValue(0);
      sb.progressBar->setVisible(false);
      ui.reloadStopWidget->setCurrentIndex(1);
      ui.stopToolButton->setEnabled(false);
      ui.actionStop->setEnabled(false);
      ui.backToolButton->setEnabled(p->canGoBack());
      ui.forwardToolButton->setEnabled(p->canGoForward());

      if(!ok)
	if(p->url().toString(QUrl::StripTrailingSlash) ==
	   p->webviewUrl().toString(QUrl::StripTrailingSlash))
	  {
	    ui.locationLineEdit->setIcon(p->icon());
	    ui.locationLineEdit->setBookmarkColor(p->isBookmarked());
	    sb.statusLabel->setText(p->statusMessage());
	  }
    }

  if(!ok)
    {
      if(p)
	{
	  int index = ui.tabWidget->indexOf(p);

	  ui.tabWidget->animateIndex(index, false, p->webviewIcon());

	  if(p->tabAction())
	    p->tabAction()->setIcon(p->webviewIcon());

	  if(p->isDir() &&
	     p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
	    slotOpenUrl();
	}

      return;
    }

  if(p)
    {
      if(p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
	{
	  ui.locationLineEdit->setBookmarkButtonEnabled(true);
	  ui.locationLineEdit->setBookmarkColor(p->isBookmarked());

	  if(ui.bookmarksMenu->actions().size() > 0)
	    ui.bookmarksMenu->actions().at(0)->setEnabled(true);

	  ui.locationLineEdit->setIcon(p->icon());
	  sb.statusLabel->setText(p->statusMessage());
	}

      int index = ui.locationLineEdit->findText
	(p->url().toString(QUrl::StripTrailingSlash));

      if(index > -1)
	ui.locationLineEdit->setItemIcon(index, p->icon());

      ui.tabWidget->animateIndex(ui.tabWidget->indexOf(p), false, p->icon());

      QString title(p->title());

      if(title.isEmpty())
	title = p->url().toString(QUrl::StripTrailingSlash);

      if(title.isEmpty())
	title = tr("(Untitled)");

      ui.tabWidget->setTabText(ui.tabWidget->indexOf(p),
			       title.replace("&", "&&"));
      ui.tabWidget->setTabToolTip(ui.tabWidget->indexOf(p), title);

      QAction *action = p->tabAction();

      if(action)
	{
	  action->setData(p->url());
	  action->setIcon(p->icon());

	  title = p->title();

	  if(title.isEmpty())
	    title = p->url().toString(QUrl::StripTrailingSlash);

	  title = dmisc::elidedTitleText(title);

	  if(title.isEmpty())
	    title = tr("(Untitled)");

	  action->setText(title);
	}

      QWebHistoryItem item(p->page()->history()->currentItem());

      if(item.isValid() &&
	 dmisc::isSchemeAcceptedByDooble(item.url().scheme()))
	{
	  QBuffer buffer;
	  QByteArray bytes;

	  buffer.setBuffer(&bytes);
	  buffer.open(QIODevice::WriteOnly);

	  QDataStream out(&buffer);

	  out << dmisc::iconForUrl(item.url());
	  buffer.close();

	  dsavehistorythread *thread = new dsavehistorythread
	    (this,
	     item.url(),
	     item.title(),
	     item.userData(),
	     bytes,
	     dmisc::passphraseWasAuthenticated() ? 0 : 1,
	     dooble::s_settings.value("settingsWindow/rememberHistory",
				      true).toBool());

	  connect(thread,
		  SIGNAL(finished(void)),
		  thread,
		  SLOT(deleteLater(void)));
	  connect(thread,
		  SIGNAL(destroyed(void)),
		  this,
		  SIGNAL(bookmarkUpdated(void)));
	  thread->start();

	  if(!p->url().host().isEmpty())
	    s_mostVisitedHosts[p->url().host()] += 1;
	  else
	    s_mostVisitedHosts
	      [p->url().toString(QUrl::StripTrailingSlash)] += 1;

	  prepareMostVisited();
	}
    }

  /*
  ** Add items to the history menu.
  */

  if(p && dmisc::isSchemeAcceptedByDooble(p->url().scheme()) &&
     s_settings.value("settingsWindow/rememberHistory",
		      true).toBool())
    {
      int index = -1;
      bool actionExists = false;
      QString title(p->title());
      QList<QAction *> actions = ui.historyMenu->actions();

      if(title.isEmpty())
	title = p->url().toString(QUrl::StripTrailingSlash);

      title = dmisc::elidedTitleText(title);

      if(title.isEmpty())
	title = tr("Dooble Web Browser");

      if(actions.size() >= 6)
	for(int i = 5; i < actions.size(); i++)
	  if(actions.at(i)->data().toUrl().
	     toString(QUrl::StripTrailingSlash) ==
	     p->url().toString(QUrl::StripTrailingSlash))
	    {
	      index = i;
	      actions.at(i)->setIcon(p->icon());
	      actions.at(i)->setText(title);
	      actionExists = true;
	      break;
	    }

      if(!actionExists)
	{
	  if(ui.historyMenu->actions().size() == 4)
	    {
	      ui.historyMenu->addSeparator();
	      ui.historyMenu->actions().at(0)->setEnabled
		(ui.historyMenu->isEnabled());
	      ui.historyMenu->actions().at(1)->setEnabled(true);
	    }

	  /*
	  ** 5 = Clear History, Show History, a separator,
	  **     Recently-Closed Tabs, and a separator.
	  */

	  QAction *action = 0;

	  if(ui.historyMenu->actions().size() >= 5 + MAX_HISTORY_ITEMS)
	    {
	      action = ui.historyMenu->actions().value(5); /*
							   ** Notice use of
							   ** value().
							   */

	      for(int i = ui.historyMenu->actions().size() - 1; i >= 6; i--)
		{
		  ui.historyMenu->actions().at(i)->setData
		    (ui.historyMenu->actions().at(i - 1)->data());
		  ui.historyMenu->actions().at(i)->setIcon
		    (ui.historyMenu->actions().at(i - 1)->icon());
		  ui.historyMenu->actions().at(i)->setText
		    (ui.historyMenu->actions().at(i - 1)->text());
		}
	    }
	  else
	    {
	      action = new QAction(this);
	      connect(action, SIGNAL(triggered(void)), this,
		      SLOT(slotLinkActionTriggered(void)));

	      if(ui.historyMenu->actions().size() == 5)
		ui.historyMenu->addAction(action);
	      else if(ui.historyMenu->actions().size() >= 6)
		ui.historyMenu->insertAction
		  (ui.historyMenu->actions().at(5), action);
	    }

	  if(action)
	    {
	      action->setEnabled(ui.historyMenu->isEnabled());
	      action->setData(p->url());
	      action->setIcon(p->icon());
	      action->setText(title);
	    }
	}
      else
	{
	  /*
	  ** We need to promote the action's index.
	  */

	  QAction *action = ui.historyMenu->actions().value(index);

	  if(action)
	    {
	      ui.historyMenu->removeAction(action);
	      ui.historyMenu->insertAction
		(ui.historyMenu->actions().value(5), action); /*
							      ** Notice use
							      ** of value().
							      */
	    }
	}
    }

  if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      if(!ui.locationLineEdit->hasFocus() &&
	 !ui.locationLineEdit->isModified() &&
	 !p->isDir() &&
	 !p->url().isEmpty() && p->url().isValid())
	p->setFocus();

      if(p->isDir())
	slotOpenUrl();
    }
}

void dooble::prepareNavigationButtonMenus(dview *p, QMenu *menu)
{
  if(!p || !menu)
    return;

  if(!s_settings.value("settingsWindow/rememberHistory", true).toBool())
    return;

  if(menu == ui.backToolButton->menu())
    {
      ui.backToolButton->menu()->clear();

      QList<QWebHistoryItem> list(p->backItems(MAX_HISTORY_ITEMS));

      for(int i = list.size() - 1; i >= 0; i--)
	{
	  QUrl url(list.at(i).url());
	  QString title(list.at(i).title());
	  QString scheme(url.scheme().toLower().trimmed());

	  if(scheme.startsWith("dooble"))
	    url.setScheme(scheme.mid(qstrlen("dooble")));

	  title = dmisc::elidedTitleText(title);

	  if(title.isEmpty())
	    title = dmisc::elidedTitleText
	      (url.toString(QUrl::StripTrailingSlash));

	  if(title.isEmpty())
	    title = tr("Dooble Web Browser");

	  QIcon icon(dmisc::iconForUrl(url));
	  QAction *action = ui.backToolButton->menu()->addAction
	    (icon, title, this, SLOT(slotGoToBackHistoryItem(void)));

	  if(action)
	    action->setData(i);
	}
    }

  if(menu == ui.forwardToolButton->menu())
    {
      ui.forwardToolButton->menu()->clear();

      QList<QWebHistoryItem> list(p->forwardItems(MAX_HISTORY_ITEMS));

      for(int i = 0; i < list.size(); i++)
	{
	  QUrl url(list.at(i).url());
	  QString title(list.at(i).title());
	  QString scheme(url.scheme().toLower().trimmed());

	  if(scheme.startsWith("dooble"))
	    url.setScheme(scheme.mid(qstrlen("dooble")));

	  if(title.isEmpty())
	    title = url.toString(QUrl::StripTrailingSlash);

	  title = dmisc::elidedTitleText(title);

	  if(title.isEmpty())
	    title = dmisc::elidedTitleText
	      (url.toString(QUrl::StripTrailingSlash));

	  if(title.isEmpty())
	    title = tr("Dooble Web Browser");

	  QIcon icon(dmisc::iconForUrl(url));
	  QAction *action = ui.forwardToolButton->menu()->addAction
	    (icon, title, this, SLOT(slotGoToForwardHistoryItem(void)));

	  if(action)
	    action->setData(i);
	}
    }
}

void dooble::slotGoHome(void)
{
  if(QApplication::keyboardModifiers() == Qt::ControlModifier)
    {
      QStringList allKeys = s_settings.keys();

      for(int i = 0; i < allKeys.size(); i++)
	{
	  if(!(allKeys.at(i).startsWith("settingsWindow/url") ||
	       allKeys.at(i).startsWith("settingsWindow/myRetrievedFiles")))
	    continue;

	  bool urlFound = false;
	  QUrl url
	    (QUrl::fromUserInput(s_settings.value(allKeys.at(i),
						  "").toString().trimmed()));

	  url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));

	  for(int j = 0; j < ui.tabWidget->count(); j++)
	    {
	      dview *p = qobject_cast<dview *> (ui.tabWidget->widget(j));

	      if(p && p->url().toString(QUrl::StripTrailingSlash) ==
		 url.toString(QUrl::StripTrailingSlash))
		{
		  urlFound = true;
		  break;
		}
	    }

	  if(!urlFound)
	    {
	      newTab(url);
	      ui.tabWidget->update();
	    }
	}
    }
  else
    {
      bool urlFound = false;
      QUrl url
	(QUrl::fromUserInput(s_settings.value("settingsWindow/homeUrl"
					      "").toString().trimmed()));

      url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));

      for(int j = 0; j < ui.tabWidget->count(); j++)
	{
	  dview *p = qobject_cast<dview *> (ui.tabWidget->widget(j));

	  if(p && p->url().toString(QUrl::StripTrailingSlash) ==
	     url.toString(QUrl::StripTrailingSlash))
	    {
	      urlFound = true;
	      ui.tabWidget->setCurrentWidget(p);
	      break;
	    }
	}

      if(!urlFound)
	{
	  dview *p = newTab(url);

	  ui.tabWidget->update();
	  ui.tabWidget->setCurrentWidget(p);
	}
    }
}

void dooble::closeTab(const int index)
{
  int count = ui.tabWidget->count();

  if(count == 1 && s_instances <= 1)
    {
      slotQuit();
      return;
    }
  else if(count == 1)
    {
      close();
      return;
    }

  if(index < 0)
    return;

  dview *p = qobject_cast<dview *> (ui.tabWidget->widget(index));

  if(p && p->isModified() &&
     s_settings.value("settingsWindow/warnBeforeClosingModifiedTab", false).
     toBool())
    {
      ui.tabWidget->setCurrentWidget(p);

      QMessageBox mb(this);

      mb.setIcon(QMessageBox::Question);
      mb.setWindowTitle(tr("Dooble Web Browser: Confirmation"));
      mb.setWindowModality(Qt::WindowModal);
      mb.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
      mb.setText(tr("Are you sure that you wish to close this modified "
		    "tab?"));

      QSettings settings(s_settings.value("iconSet").toString(),
			 QSettings::IniFormat);

      for(int i = 0; i < mb.buttons().size(); i++)
	if(mb.buttonRole(mb.buttons().at(i)) == QMessageBox::YesRole)
	  {
	    mb.buttons().at(i)->setIcon
	      (QIcon(settings.value("okButtonIcon").toString()));
	    mb.buttons().at(i)->setIconSize(QSize(16, 16));
	  }
	else
	  {
	    mb.buttons().at(i)->setIcon
	      (QIcon(settings.value("cancelButtonIcon").toString()));
	    mb.buttons().at(i)->setIconSize(QSize(16, 16));
	  }

      mb.setWindowIcon
	(QIcon(settings.value("mainWindow/windowIcon").toString()));

      if(mb.exec() == QMessageBox::No)
	return;
    }

  if(count > 1)
    {
      if(p)
	{
	  if(!p->url().isEmpty() && p->url().isValid() &&
	     s_settings.value("settingsWindow/rememberClosedTabs",
			      false).toBool())
	    {
	      m_closedTabs.prepend(p->history());

	      QString title(p->title());

	      if(title.isEmpty())
		title = p->url().toString(QUrl::StripTrailingSlash);

	      title = dmisc::elidedTitleText(title);

	      if(title.isEmpty())
		title = tr("Dooble Web Browser");

	      QAction *action = new QAction(p->icon(), title, this);

	      connect(action, SIGNAL(triggered(void)), this,
		      SLOT(slotReopenClosedTab(void)));

	      if(ui.historyMenu->actions().size() >= 4 &&
		 !ui.historyMenu->actions().at(3)->menu())
		ui.historyMenu->actions().at(3)->setMenu(new QMenu(this));

	      if(ui.historyMenu->actions().size() >= 4)
		{
		  if(ui.historyMenu->actions().at(3)->menu()->
		     actions().isEmpty())
		    {
		      QSettings settings
			(s_settings.value("iconSet").toString(),
			 QSettings::IniFormat);

		      ui.historyMenu->actions().at(3)->menu()->addAction
			(QIcon(settings.value("mainWindow/historyMenu").
			       toString()), tr("&Clear"), this,
			 SLOT(slotClearRecentlyClosedTabs(void)));
		      ui.historyMenu->actions().at(3)->menu()->addSeparator();
		    }

		  ui.historyMenu->actions().at(3)->menu()->insertAction
		    (ui.historyMenu->actions().at(3)->menu()->actions().
		     value(2), action);
		}

	      /*
	      ** Enable the Clear History action.
	      */

	      if(ui.historyMenu->actions().size() >= 1)
		ui.historyMenu->actions().at(0)->setEnabled(true);

	      /*
	      ** Enable the Recently-Closed Tabs menu.
	      */

	      if(ui.historyMenu->actions().size() >= 4)
		ui.historyMenu->actions().at(3)->setEnabled(true);

	      /*
	      ** Remove outliers.
	      */

	      int number = s_settings.value
		("settingsWindow/rememberClosedTabsCount", 1).toInt();

	      if(number < 1 || number > MAX_HISTORY_ITEMS)
                 number = 1;

	      for(int i = m_closedTabs.size() - 1; i >= number; i--)
		{
		  m_closedTabs.removeAt(i);

		  if(ui.historyMenu->actions().size() >= 4 &&
		     ui.historyMenu->actions().at(3)->menu() &&
		     ui.historyMenu->actions().at(3)->menu()->actions().
		     size() > 2)
		    ui.historyMenu->actions().at(3)->menu()->actions().
		      value(i + 2)->deleteLater();
		}
	    }

	  /*
	  ** The status bar should be reset if the user closed
	  ** a tab while the mouse cursor was on a link. We should only
	  ** clear the text if the tab that was closed was the current tab.
	  */

	  if(p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
	    sb.statusLabel->clear();

	  p->deleteLater();
	}
      else if(qobject_cast<ddesktopwidget *> (ui.tabWidget->widget(index)))
	closeDesktop();
      else if(qobject_cast<dreinstatedooble *> (ui.tabWidget->widget(index)))
	{
	  dreinstatedooble *reinstateWidget = qobject_cast
	    <dreinstatedooble *> (ui.tabWidget->widget(index));

	  if(reinstateWidget)
	    reinstateWidget->deleteLater();
	}
      else
	{
	  dplugintab *plugTab = qobject_cast<dplugintab *>
	    (ui.tabWidget->widget(index));

	  if(plugTab)
	    slotPluginExiting(plugTab->extension(), 0);
	}

      count -= 1;
    }

  ui.tabWidget->setTabsClosable(count > 1);

  if(count == 1)
    ui.tabWidget->setBarVisible
      (s_settings.value("settingsWindow/alwaysShowTabBar",
			true).toBool());
  else
    ui.tabWidget->setBarVisible(true);

  ui.tabWidget->update();
}

void dooble::slotCloseTab(void)
{
  closeTab(ui.tabWidget->currentIndex());
}

void dooble::slotCloseTab(const int index)
{
  closeTab(index);
}

void dooble::slotOpenUrl(void)
{
  if(isFullScreen())
    slotMarkerEntered();

  ui.locationLineEdit->setFocus();
  ui.locationLineEdit->selectAll();
  update();
}

void dooble::slotGoToBackHistoryItem(void)
{
  QAction *action = qobject_cast<QAction *> (sender());

  if(action)
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

      if(p)
	{
	  int index = action->data().toInt();
	  QList<QWebHistoryItem> list(p->backItems(MAX_HISTORY_ITEMS));

	  if(index >= 0 && index < list.size())
	    p->goToItem(list.at(index));
	}
    }
}

void dooble::slotGoToForwardHistoryItem(void)
{
  QAction *action = qobject_cast<QAction *> (sender());

  if(action)
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

      if(p)
	{
	  int index = action->data().toInt();
	  QList<QWebHistoryItem> list(p->forwardItems(MAX_HISTORY_ITEMS));

	  if(index >= 0 && index < list.size())
	    p->goToItem(list.at(index));
	}
    }
}

void dooble::slotLinkActionTriggered(void)
{
  QAction *action = qobject_cast<QAction *> (sender());

  if(action)
    {
      bool doLoad = true;
      QList<QWidget *> list(action->associatedWidgets());

      for(int i = 0; i < list.size(); i++)
	if(qobject_cast<dview *> (list.at(i)))
	  {
	    doLoad = false;
	    ui.tabWidget->setCurrentWidget(list.at(i));
	    break;
	  }

      if(doLoad)
	{
	  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

	  if(p && p->isModified() && !warnAboutLeavingModifiedTab())
	    return;

	  loadPage(action->data().toUrl());

	  if(ui.tabWidget->currentWidget())
	    ui.tabWidget->currentWidget()->setFocus();
	}
    }
}

void dooble::slotLoadStarted(void)
{
  dview *p = qobject_cast<dview *> (sender());

  if(p)
    ui.tabWidget->animateIndex(ui.tabWidget->indexOf(p), true, QIcon());

  if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      ui.locationLineEdit->setBookmarkButtonEnabled(false);
      ui.locationLineEdit->setIcon(dmisc::iconForUrl(QUrl()));

      if(ui.bookmarksMenu->actions().size() > 0)
	ui.bookmarksMenu->actions().at(0)->setEnabled(false);

      if(p->tabAction())
	p->tabAction()->setIcon(dmisc::iconForUrl(QUrl()));

      if(p->isDir() || p->isFtp())
	sb.progressBar->setMaximum(0);
      else
	sb.progressBar->setMaximum(100);

      sb.statusLabel->clear();
      sb.progressBar->setValue(0);
      sb.progressBar->setVisible(true);
      ui.reloadStopWidget->setCurrentIndex(0);
      ui.stopToolButton->setEnabled(true);
      ui.actionStop->setEnabled(true);
      ui.backToolButton->setEnabled(p->canGoBack());
      ui.forwardToolButton->setEnabled(p->canGoForward());
      ui.locationLineEdit->setSecureColor(false);
      ui.locationLineEdit->setBookmarkColor(p->isBookmarked());
    }
}

void dooble::slotSearch(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      bool post = false;
      QString urlText("");
      QString postText("");
      QString searchName(ui.searchLineEdit->type());

      if(searchName == "bing")
	urlText = QString("http://www.bing.com/search?q=%1").arg
	  (ui.searchLineEdit->text().trimmed().replace(" ", "+"));
      else if(searchName == "dogpile")
	urlText = QString("http://www.dogpile.com/dogpile_other/ws/results/"
			  "Web/%1/1/417/TopNavigation/Relevance/iq=true/"
			  "zoom=off/_iceUrlFlag=7?_IceUrl=true").arg
	  (ui.searchLineEdit->text().trimmed());
      else if(searchName == "duckduckgo")
	/*
	** kd=1: Redirect (Prevent Information Sharing)
	** kg=p: Post
	** kh=1: HTTPS
	** kp=1: Safe Search
	*/

	urlText = QString("https://www.duckduckgo.com?q=%1&"
			  "kd=1&kg=p&kh=1&kp=1").
	  arg(ui.searchLineEdit->text().trimmed());
      else if(searchName == "google")
        urlText = QString("https://www.google.com/search?q=%1").arg
	  (ui.searchLineEdit->text().trimmed().replace(" ", "+"));
      else if(searchName == "ixquick")
	{
	  post = true;
	  urlText = "https://www.ixquick.com/do/metasearch.pl";
	  postText = QString("query=%1").
	    arg(ui.searchLineEdit->text().trimmed());
	}
      else if(searchName == "history")
	{
	  ui.actionShow_HistorySideBar->setChecked(true);
	  m_historySideBar->search(ui.searchLineEdit->text().toLower().
				   trimmed());
	  return;
	}
      else if(searchName == "wikipedia")
	{
	  QLocale locale;

	  urlText = QString
            ("https://%1.wikipedia.org/wiki/Special:"
	     "Search?search=%2").
	    arg(locale.name().left(2)).
	    arg(ui.searchLineEdit->text().trimmed().replace(" ", "+"));
	}
      else if(searchName == "wikinews")
	{
	  QLocale locale;

	  urlText = QString
	    ("https://secure.wikimedia.org/wikinews/%1/w/index.php?"
	     "search=%2").
	    arg(locale.name().left(2)).
	    arg(ui.searchLineEdit->text().trimmed().replace(" ", "+"));
	}
      else if(searchName == "wolframalpha")
	urlText = QString("http://www.wolframalpha.com/input/?i=%1").
	  arg(ui.searchLineEdit->text().trimmed().replace(" ", "+"));
      else if(searchName == "yacy")
        urlText = QString
	  ("http://localhost:8080/yacysearch.html?display=2&count=100"
	   "&resource=global&query=%1").arg
	  (ui.searchLineEdit->text().trimmed().replace(" ", "+"));
      else
	/*
	** Unknown!
	*/

	return;

      QUrl url(QUrl::fromUserInput(urlText));

      url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));

      if(post)
	p->post(url, postText);
      else
	p->load(url);
    }
}

void dooble::slotCopyLink(const QUrl &url)
{
  QApplication::clipboard()->setText(url.toString(QUrl::StripTrailingSlash));
}

void dooble::slotOpenLinkInNewTab(const QUrl &url)
{
  dview *p = newTab(url);

  if(p)
    {
      if(s_settings.value("settingsWindow/proceedToNewTab",
			  true).toBool())
	ui.tabWidget->setCurrentWidget(p);

      ui.tabWidget->update();
    }
}

void dooble::slotOpenLinkInNewWindow(const QUrl &url)
{
  if(s_settings.value("settingsWindow/openUserWindowsInNewProcesses",
		      false).toBool())
    launchDooble(url);
  else
    {
      Q_UNUSED(new dooble(url, this));
    }
}

void dooble::slotOpenPageInNewWindow(const int index)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->widget(index));

  if(p)
    {
      int count = ui.tabWidget->count() - 1;

      if(s_settings.value("settingsWindow/openUserWindowsInNewProcesses",
			  false).toBool())
	{
	  launchDooble(p->url());
	  p->deleteLater();
	}
      else
	{
	  Q_UNUSED(new dooble(p, this));
	}

      ui.tabWidget->setTabsClosable(count > 1);
    }
}

void dooble::slotLinkHovered(const QString &link, const QString &title,
			     const QString &textContent)
{
  Q_UNUSED(title);
  Q_UNUSED(textContent);

  if(link.isEmpty())
    sb.statusLabel->clear();
  else
    sb.statusLabel->setText(link.trimmed().mid(0, 100));
}

void dooble::slotAbout(void)
{
  QMessageBox *mb = findChild<QMessageBox *> ("about");

  if(!mb)
    {
      mb = new QMessageBox(this);
      mb->setObjectName("about");
    }

  mb->setWindowModality(Qt::NonModal);
  mb->setStyleSheet("QMessageBox {background: white;}");
  mb->setWindowTitle(tr("Dooble Web Browser: About"));
  mb->setTextFormat(Qt::RichText);
  mb->setText
    (QString("<html>"
             "Dooble Web Browser<br><br>"
             "Version %1 Satchmo.<br>"
	     "Copyright (c) 2008 - 2012 Bad News Fred.<br>"
	     "Qt version %3."
	     "<hr>"
	     "Please visit <a href='http://dooble.sf.net'>"
	     "http://dooble.sf.net</a> for more information."
	     "<hr>"
	     "Interested in the latest "
	     "<a href='http://dooble.svn.sourceforge.net/"
	     "viewvc/dooble/trunk/browser/Doc/RELEASE-NOTES'>"
	     "release notes</a>?"
	     "</html>").
     arg(DOOBLE_VERSION_STR).
     arg(QT_VERSION_STR));
  mb->setStandardButtons(QMessageBox::Ok);

  QSettings settings
    (s_settings.value("iconSet").toString(), QSettings::IniFormat);

  mb->setWindowIcon
    (QIcon(settings.value("mainWindow/windowIcon").toString()));

  for(int i = 0; i < mb->buttons().size(); i++)
    if(mb->buttonRole(mb->buttons().at(i)) == QMessageBox::AcceptRole)
      {
	mb->buttons().at(i)->setIcon
	  (QIcon(settings.value("okButtonIcon").toString()));
	mb->buttons().at(i)->setIconSize(QSize(16, 16));
      }

  mb->setIconPixmap(QPixmap("Icons/AxB/dooble.png"));
  mb->show();
}

void dooble::slotSavePage(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      if(p->isDir() || p->isFtp() ||
	 p->url().scheme().toLower().trimmed() == "qrc")
	saveHandler(p->url(), p->html(), "", 0);
      else
	saveHandler(p->url(), "", "", 0);
    }
}

void dooble::slotSaveUrl(const QUrl &url, const int choice)
{
  saveHandler(url, "", "", choice);
}

void dooble::slotSaveFile(const QString &fileName, const QUrl &url,
			  const int choice)
{
  saveHandler(url, "", fileName, choice);
}

void dooble::saveHandler(const QUrl &url, const QString &html,
			 const QString &fileName,
			 const int choice)
{
  QString path
    (s_settings.value("settingsWindow/myRetrievedFiles", "").toString());
  QWidget *parent = qobject_cast<QWidget *> (sender());
  QFileInfo fileInfo(path);

  if(!fileInfo.isReadable() || !fileInfo.isWritable())
    path = QDesktopServices::storageLocation
      (QDesktopServices::DesktopLocation);

  if(!parent)
    parent = this;

  QString l_fileName(fileName);

  /*
  ** This fileDialog business is troublesome. At times,
  ** when the dialog is closed, Dooble will terminate.
  */

  QFileDialog *fileDialog = new QFileDialog(parent);

#ifdef Q_WS_MAC
  fileDialog->setAttribute(Qt::WA_MacMetalStyle, false);
#endif
  fileDialog->setWindowTitle(tr("Dooble Web Browser: Save As"));
  fileDialog->setFileMode(QFileDialog::AnyFile);
  fileDialog->setDirectory(path);
  fileDialog->setLabelText(QFileDialog::Accept, tr("&Save"));
  fileDialog->setAcceptMode(QFileDialog::AcceptSave);

#ifdef Q_WS_MAC
  /*
  ** Attempt to find the QLineEdit widget.
  */

  QLineEdit *lineEdit = fileDialog->findChild<QLineEdit *> ("fileNameEdit");

  if(!lineEdit)
    lineEdit = fileDialog->findChild<QLineEdit *> ();
#endif

  if(url.path().isEmpty() || url.path() == "/")
#ifdef Q_WS_MAC
    {
      if(lineEdit)
	{
	  if(l_fileName.isEmpty())
	    l_fileName = "dooble.download";

	  l_fileName = dmisc::findUniqueFileName
	    (l_fileName, fileDialog->directory());
	  lineEdit->setText(l_fileName);
	}
    }
#else
    {
      if(l_fileName.isEmpty())
	l_fileName = "dooble.download";

      l_fileName = dmisc::findUniqueFileName
	(l_fileName, fileDialog->directory());
      fileDialog->selectFile(l_fileName);
    }
#endif
  else if(url.path().contains("/"))
    {
      if(l_fileName.isEmpty())
	l_fileName = QFileInfo(url.path()).fileName();

      if(l_fileName.isEmpty())
	l_fileName = "dooble.download";

      l_fileName = dmisc::findUniqueFileName
	(l_fileName, fileDialog->directory());

#ifdef Q_WS_MAC
      if(lineEdit)
	lineEdit->setText(l_fileName);
#else
      fileDialog->selectFile(l_fileName);
#endif
    }

  /*
  ** We sometimes terminate on this very spot.
  ** Are we not allowed to start an event loop
  ** from a function that is called by a slot?
  */

  /*
  ** Now for a workaround.
  */

  connect(fileDialog,
	  SIGNAL(finished(int)),
	  this,
	  SLOT(slotHandleQuirkySaveDialog(int)));
  m_saveDialogHackContainer.url = url;
  m_saveDialogHackContainer.html = html;
  m_saveDialogHackContainer.choice = choice;
  fileDialog->setWindowModality(Qt::WindowModal);
  fileDialog->show();
}

void dooble::slotHandleQuirkySaveDialog(int result)
{
  QFileDialog *fileDialog = qobject_cast<QFileDialog *> (sender());

  if(!fileDialog)
    return;

  /*
  ** You should not be here when their parents are out!
  */

  if(result == QDialog::Accepted)
    {
      QStringList list(fileDialog->selectedFiles());

      if(!list.isEmpty())
	{
	  s_downloadWindow->show();

	  QString fileName(QFileInfo(list.value(0)).absoluteFilePath());

	  if(!m_saveDialogHackContainer.html.isEmpty())
	    s_downloadWindow->addHtmlItem
	      (m_saveDialogHackContainer.html, fileName);
	  else if(!m_saveDialogHackContainer.url.scheme().toLower().trimmed().
		  startsWith("file"))
	    s_downloadWindow->addUrlItem
	      (m_saveDialogHackContainer.url, fileName, true,
	       QDateTime::currentDateTime(),
	       m_saveDialogHackContainer.choice);
	  else
	    s_downloadWindow->addFileItem
	      (m_saveDialogHackContainer.url.toLocalFile(), fileName,
	       true, QDateTime::currentDateTime());
	}
    }

  fileDialog->deleteLater();
}

void dooble::slotDisplayDownloadWindow(void)
{
  s_downloadWindow->show();
}

void dooble::slotShowFind(void)
{
  if(qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

      if(p && (p->isDir() || p->isFtp()))
	ui.findFrame->setVisible(false);
      else
	{
	  showFindFrame = true;
	  ui.findFrame->setVisible(true);
	}

      if(p)
	ui.actionFind->setEnabled(!p->isDir() && !p->isFtp());

      if(ui.tabWidget->isVisible())
	{
	  ui.findLineEdit->setFocus();
	  ui.findLineEdit->selectAll();

#ifdef Q_WS_MAC
	  static int fixed = 0;

	  if(!fixed)
	    {
	      QColor color(255, 255, 255);
	      QPalette palette(ui.findLineEdit->palette());

	      palette.setColor(ui.findLineEdit->backgroundRole(), color);
	      ui.findLineEdit->setPalette(palette);
	      fixed = 1;
	    }
#endif
	  update();
	}
    }
}

void dooble::slotHideFind(void)
{
  showFindFrame = false;
  ui.findFrame->setVisible(false);
}

void dooble::slotFindNext(void)
{
  if(ui.matchCaseCheckBox->isChecked())
    find(QWebPage::FindWrapsAroundDocument | QWebPage::FindCaseSensitively);
  else
    find(QWebPage::FindWrapsAroundDocument);
}

void dooble::slotFindPrevious(void)
{
  if(ui.matchCaseCheckBox->isChecked())
    find(QWebPage::FindBackward | QWebPage::FindWrapsAroundDocument |
	 QWebPage::FindCaseSensitively);
  else
    find(QWebPage::FindBackward | QWebPage::FindWrapsAroundDocument);
}

void dooble::slotFind(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      p->page()->findText(QString(), QWebPage::HighlightAllOccurrences);

      if(ui.highlightAllCheckBox->isChecked())
	find(QWebPage::HighlightAllOccurrences);
    }
}

void dooble::find(QWebPage::FindFlags flags)
{
  QString text(ui.findLineEdit->text());
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());
  static QPalette lineEditPalette(ui.findLineEdit->palette());

  if(p && !p->page()->findText(text, flags))
    {
      if(!text.isEmpty())
	{
	  QColor color(240, 128, 128); // Light Coral
	  QPalette palette(ui.findLineEdit->palette());

	  palette.setColor(ui.findLineEdit->backgroundRole(), color);
	  ui.findLineEdit->setPalette(palette);
	}
      else
	ui.findLineEdit->setPalette(lineEditPalette);
    }
  else if(p)
    ui.findLineEdit->setPalette(lineEditPalette);
}

void dooble::keyPressEvent(QKeyEvent *event)
{
  if(event)
    {
      bool processed = false;

      if(QKeySequence(event->modifiers() + event->key()) ==
	 QKeySequence(Qt::ControlModifier + Qt::Key_B))
	{
	  processed = true;
	  slotShowBookmarks();
	}
      else if(event->key() == Qt::Key_Escape && ui.findFrame->isVisible())
	{
	  processed = true;
	  showFindFrame = false;
	  ui.findFrame->setVisible(false);
	}
      else if(event->key() == Qt::Key_Escape)
	{
	  processed = true;
	  slotStop();

	  if(isFullScreen())
	    slotViewEntered();
	}
      else if(event->key() == Qt::Key_X &&
	      event->modifiers() == Qt::AltModifier)
	{
	  processed = true;
	  showMinimized();
	}
      else if(QKeySequence(event->modifiers() + event->key()) ==
	      QKeySequence(Qt::ControlModifier + Qt::ShiftModifier +
			   Qt::Key_Plus))
	{
	  processed = true;
	  ui.actionZoom_In->trigger();
	}

      if(!processed && (isFullScreen() || m_isJavaScriptWindow))
	{
	  /*
	  ** OK, this is going to be tricky.
	  ** Grab all the actions.
	  ** For every action that is enabled, determine
	  ** its shortcuts. If a shortcut matches the event's key
	  ** and the event's modifier, issue the action's trigger() method.
	  */

#ifdef Q_WS_X11
	  if(QKeySequence(event->modifiers() + event->key()) ==
	     QKeySequence(Qt::ControlModifier + Qt::Key_Equal) ||
	     QKeySequence(event->modifiers() + event->key()) ==
	     QKeySequence(Qt::ControlModifier + Qt::ShiftModifier +
			  Qt::Key_Plus))
	    {
	      ui.actionZoom_In->trigger();
	      return;
	    }
#endif

	  QKeySequence shortcut(event->modifiers() + event->key());

	  foreach(QAction *action, findChildren<QAction *> ())
	    if(!action->isSeparator() && action->isEnabled())
	      {
		QList<QKeySequence> shortcuts(action->shortcuts());

		for(int i = 0; i < shortcuts.size(); i++)
		  if(shortcut == shortcuts.at(i))
		    {
		      action->trigger();
		      break;
		    }
	      }
	}
    }

  QMainWindow::keyPressEvent(event);
}

void dooble::slotPrint(void)
{
  QPrinter printer;
  QPrintDialog printDialog(&printer, this);

#ifdef Q_WS_MAC
  printDialog.setAttribute(Qt::WA_MacMetalStyle, false);
#endif

  if(printDialog.exec() == QDialog::Accepted)
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

      if(p)
	p->print(&printer);
    }
}

void dooble::slotPrintRequested(QWebFrame *frame)
{
  if(frame)
    {
      QPrinter printer;
      QPrintDialog printDialog(&printer, this);

#ifdef Q_WS_MAC
      printDialog.setAttribute(Qt::WA_MacMetalStyle, false);
#endif

      if(printDialog.exec() == QDialog::Accepted)
	frame->print(&printer);
    }
}

void dooble::slotPrintPreview(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      QPrinter printer;
      QPrintPreviewDialog printDialog(&printer, this);

#ifdef Q_WS_MAC
      printDialog.setAttribute(Qt::WA_MacMetalStyle, false);
#endif
      printDialog.setWindowModality(Qt::WindowModal);
      connect(&printDialog,
	      SIGNAL(paintRequested(QPrinter *)),
	      p->currentFrame(),
	      SLOT(print(QPrinter *)));

      if(printDialog.exec() == QDialog::Accepted)
	p->print(&printer);
    }
}

void dooble::slotShowDesktopTab(const bool state)
{
  if(!m_desktopWidget)
    {
      m_desktopWidget = new ddesktopwidget(this);
      connect(m_desktopWidget,
	      SIGNAL(viewEntered(void)),
	      this,
	      SLOT(slotViewEntered(void)));
      connect(m_desktopWidget,
	      SIGNAL(backgroundImageChanged(void)),
	      this,
	      SLOT(slotChangeDesktopBackgrounds(void)));

      QSettings settings
	(s_settings.value("iconSet").toString(), QSettings::IniFormat);

      settings.beginGroup("mainWindow");

      int index = -1;

      if(s_settings.value("settingsWindow/appendNewTabs",
			  false).toBool())
	index = ui.tabWidget->addTab
	  (m_desktopWidget,
	   QIcon(settings.value("tabWidget").toString()),
	   tr("Dooble Desktop"));
      else
	index = ui.tabWidget->insertTab
	  (ui.tabWidget->currentIndex() + 1,
	   m_desktopWidget,
	   QIcon(settings.value("tabWidget").toString()),
	   tr("Dooble Desktop"));

      ui.tabWidget->setTabToolTip(index, tr("Dooble Desktop"));
      ui.desktopToolButton->setEnabled(false);
      ui.tabWidget->setTabsClosable(ui.tabWidget->count() > 1);

      QAction *action = new QAction(tr("Dooble Desktop"), this);

      action->setData("Desktop");
      action->setIcon
	(QIcon(settings.value("actionDesktop").toString()));
      connect(action, SIGNAL(triggered(void)), this,
	      SLOT(slotShowDesktopTab(void)));
      m_desktopWidget->setTabAction(action);
      prepareTabsMenu();

      if(state)
	ui.tabWidget->setCurrentWidget(m_desktopWidget);

      if(ui.tabWidget->count() == 1)
	ui.tabWidget->setBarVisible
	  (s_settings.value("settingsWindow/alwaysShowTabBar",
			    true).toBool());
      else
	ui.tabWidget->setBarVisible(true);
    }
  else
    ui.tabWidget->setCurrentWidget(m_desktopWidget);
}

void dooble::slotShowPageSource(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p && p->currentFrame())
    {
      dpagesourcewindow *ps = new dpagesourcewindow
	(p->url(),
	 p->currentFrame()->toHtml());

      connect(this,
	      SIGNAL(iconsChanged(void)),
	      ps,
	      SLOT(slotSetIcons(void)));

      /*
      ** The dpagesourcewindow object automatically destroys
      ** itself whenever it is closed.
      */
    }
}

QList<QVariantList> dooble::tabUrls(void) const
{
  QList<QVariantList> urls;

  for(int i = 0; i < ui.tabWidget->count(); i++)
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->widget(i));

      if(p)
	{
	  QString title(p->title());

	  if(title.isEmpty())
	    title = p->url().toString(QUrl::StripTrailingSlash);

	  title = dmisc::elidedTitleText(title);

	  if(title.isEmpty())
	    title = tr("(Untitled)");

	  QVariantList list;

	  list.append(title);
	  list.append(p->url());
	  list.append(p->icon());
	  urls.append(list);
	}
    }

  return urls;
}

void dooble::slotShowSettingsWindow(void)
{
  s_settingsWindow->exec(this);
}

void dooble::slotViewZoomIn(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    p->setZoomFactor(p->zoomFactor() + 0.1);
}

void dooble::slotViewZoomOut(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());
	
  if(p)
    p->setZoomFactor(p->zoomFactor() - 0.1);
}

void dooble::slotViewResetZoom(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    p->setZoomFactor(1.0);
}

void dooble::slotViewZoomTextOnly(bool enable)
{
  s_settings["mainWindow/zoomTextOnly"] = enable;

  QSettings settings;

  settings.setValue("mainWindow/zoomTextOnly", enable);

  for(int i = 0; i < ui.tabWidget->count(); i++)
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->widget(i));

      if(p)
	p->zoomTextOnly(enable);
    }
}

void dooble::slotStatusBarDisplay(bool state)
{
  if(isFullScreen())
    return;

  statusBar()->setVisible(state);

  QSettings settings;

  settings.setValue("mainWindow/statusbarDisplay", state);
  s_settings["mainWindow/statusbarDisplay"] = state;
}

void dooble::slotShowHiddenFiles(bool state)
{
  QSettings settings;

  settings.setValue("mainWindow/showHiddenFiles", state);
  s_settings["mainWindow/showHiddenFiles"] = state;

  if(state)
    {
      dfilemanager::treeModel->setFilter
	(dfilemanager::treeModel->filter() | QDir::Hidden);
      dfilemanager::tableModel->setFilter
	(dfilemanager::tableModel->filter() | QDir::Hidden);
    }
  else
    {
      dfilemanager::treeModel->setFilter
	(dfilemanager::treeModel->filter() & ~QDir::Hidden);
      dfilemanager::tableModel->setFilter
	(dfilemanager::tableModel->filter() & ~QDir::Hidden);
    }
}

void dooble::closeDesktop(void)
{
  /*
  ** Destroy the widget that is housing the Desktop.
  */

  for(int i = 0; i < ui.tabWidget->count(); i++)
    if(qobject_cast<ddesktopwidget *> (ui.tabWidget->widget(i)))
      {
	ui.tabWidget->removeTab(i);
	ui.desktopToolButton->setEnabled(true);
	break;
      }

  for(int i = 0; i < ui.menu_Tabs->actions().size(); i++)
    if(ui.menu_Tabs->actions().at(i)->data().toString() == "Desktop")
      {
	ui.menu_Tabs->removeAction(ui.menu_Tabs->actions().at(i));
	break;
      }

  ui.tabWidget->setTabsClosable(ui.tabWidget->count() > 1);

  if(m_desktopWidget)
    m_desktopWidget->deleteLater();
}

void dooble::slotOpenDirectory(void)
{
  QFileDialog fileDialog(this);

#ifdef Q_WS_MAC
  fileDialog.setAttribute(Qt::WA_MacMetalStyle, false);
#endif
  fileDialog.setWindowModality(Qt::WindowModal);
  fileDialog.setWindowTitle(tr("Dooble Web Browser: Open Directory"));
  fileDialog.setFileMode(QFileDialog::Directory);
  fileDialog.setDirectory(QDir::homePath());
  fileDialog.setLabelText(QFileDialog::Accept, tr("&Open"));
  fileDialog.setAcceptMode(QFileDialog::AcceptOpen);

  if(fileDialog.exec() == QDialog::Accepted)
    {
      QStringList list(fileDialog.selectedFiles());

      if(!list.isEmpty())
	{
	  if(qobject_cast<ddesktopwidget *> (ui.tabWidget->currentWidget()))
	    {
	      if(m_desktopWidget)
		{
		  QUrl url(QUrl::fromLocalFile(list.at(0)));

		  url = QUrl::fromEncoded
		    (url.toEncoded(QUrl::StripTrailingSlash));
		  m_desktopWidget->showFileManagerWindow(url);
		}
	    }
	  else
	    {
	      dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

	      if(p && p->isModified() && !warnAboutLeavingModifiedTab())
		return;

	      QUrl url(QUrl::fromLocalFile(list.at(0)));

	      url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));
	      loadPage(url);

	      if(ui.tabWidget->currentWidget())
		ui.tabWidget->currentWidget()->setFocus();
	    }
	}
    }
}

void dooble::slotOpenMyRetrievedFiles(void)
{
  if(qobject_cast<ddesktopwidget *> (ui.tabWidget->currentWidget()))
    {
      if(m_desktopWidget)
	m_desktopWidget->showFileManagerWindow();
    }
  else
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

      if(p && p->isModified() && !warnAboutLeavingModifiedTab())
	return;

      QUrl url;
      QString path(s_settings.value("settingsWindow/myRetrievedFiles", "").
		   toString());

      url = QUrl::fromLocalFile(path);
      url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));
      loadPage(url);

      if(ui.tabWidget->currentWidget())
	ui.tabWidget->currentWidget()->setFocus();
    }
}

void dooble::slotChangeDesktopBackgrounds(void)
{
  foreach(QWidget *widget, QApplication::topLevelWidgets())
    if(qobject_cast<dooble *> (widget))
      {
	dooble *d = qobject_cast<dooble *> (widget);

	if(d)
	  emit d->updateDesktopBackground();
      }
}

void dooble::slotCopy(void)
{
  if(ui.searchLineEdit->hasFocus() &&
     !ui.searchLineEdit->selectedText().isEmpty())
    ui.searchLineEdit->copy();
  else if(ui.locationLineEdit->hasFocus() &&
	  !ui.locationLineEdit->selectedText().isEmpty())
    ui.locationLineEdit->copy();
  else
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

      if(p && !p->isDir() && !p->isFtp())
	p->page()->triggerAction(QWebPage::Copy);
    }
}

void dooble::slotPaste(void)
{
  if(ui.searchLineEdit->hasFocus())
    ui.searchLineEdit->paste();
  else if(ui.locationLineEdit->hasFocus())
    ui.locationLineEdit->paste();
  else
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

      if(p && !p->isDir() && !p->isFtp())
	p->page()->triggerAction(QWebPage::Paste);
    }
}

void dooble::slotSelectAllContent(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    p->page()->triggerAction(QWebPage::SelectAll);
}

void dooble::slotAuthenticationRequired(QNetworkReply *reply,
					QAuthenticator *authenticator)
{
  if(!reply || !authenticator)
    return;

  QDialog dialog(this);
  Ui_passwordDialog ui_p;

  ui_p.setupUi(&dialog);
#ifdef Q_WS_MAC
  dialog.setAttribute(Qt::WA_MacMetalStyle, false);
#endif
  ui_p.messageLabel->setText
    (QString(tr("The site %1 is requesting "
		"credentials.").
	     arg(reply->url().
		 toString(QUrl::RemovePath | QUrl::RemovePassword |
			  QUrl::RemoveQuery | QUrl::StripTrailingSlash))));

  QSettings settings(s_settings.value("iconSet").toString(),
		     QSettings::IniFormat);

  dialog.setWindowIcon
    (QIcon(settings.value("mainWindow/windowIcon").toString()));

  for(int i = 0; i < ui_p.buttonBox->buttons().size(); i++)
    if(ui_p.buttonBox->buttonRole(ui_p.buttonBox->buttons().at(i)) ==
       QDialogButtonBox::YesRole)
      {
	if(qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().at(i)))
	  qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().at(i))->
	    setDefault(true);

	ui_p.buttonBox->buttons().at(i)->setIcon
	  (QIcon(settings.value("okButtonIcon").toString()));
	ui_p.buttonBox->buttons().at(i)->setIconSize(QSize(16, 16));
      }
    else
      {
	if(qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().at(i)))
	  qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().at(i))->
	    setDefault(false);

	ui_p.buttonBox->buttons().at(i)->setIcon
	  (QIcon(settings.value("cancelButtonIcon").toString()));
	ui_p.buttonBox->buttons().at(i)->setIconSize(QSize(16, 16));
      }

  if(dialog.exec() == QDialog::Accepted)
    {
      authenticator->setUser(ui_p.usernameLineEdit->text());
      authenticator->setPassword(ui_p.passwordLineEdit->text());
    }
}

void dooble::slotProxyAuthenticationRequired(const QNetworkProxy &proxy,
					     QAuthenticator *authenticator)
{
  if(!authenticator)
    return;

  Q_UNUSED(proxy);
  QDialog dialog(this);
  Ui_passwordDialog ui_p;

  ui_p.setupUi(&dialog);
#ifdef Q_WS_MAC
  dialog.setAttribute(Qt::WA_MacMetalStyle, false);
#endif
  ui_p.messageLabel->setText
    (QString(tr("The proxy %1:%2 is requesting "
		"credentials.").
	     arg(proxy.hostName()).
	     arg(proxy.port())));

  QSettings settings(s_settings.value("iconSet").toString(),
		     QSettings::IniFormat);

  dialog.setWindowIcon
    (QIcon(settings.value("mainWindow/windowIcon").toString()));

  for(int i = 0; i < ui_p.buttonBox->buttons().size(); i++)
    if(ui_p.buttonBox->buttonRole(ui_p.buttonBox->buttons().at(i)) ==
       QDialogButtonBox::YesRole)
      {
	if(qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().at(i)))
	  qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().at(i))->
	    setDefault(true);

	ui_p.buttonBox->buttons().at(i)->setIcon
	  (QIcon(settings.value("okButtonIcon").toString()));
	ui_p.buttonBox->buttons().at(i)->setIconSize(QSize(16, 16));
      }
    else
      {
	if(qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().at(i)))
	  qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().at(i))->
	    setDefault(false);

	ui_p.buttonBox->buttons().at(i)->setIcon
	  (QIcon(settings.value("cancelButtonIcon").toString()));
	ui_p.buttonBox->buttons().at(i)->setIconSize(QSize(16, 16));
      }

  if(dialog.exec() == QDialog::Accepted)
    {
      authenticator->setUser(ui_p.usernameLineEdit->text());
      authenticator->setPassword(ui_p.passwordLineEdit->text());
    }
}

void dooble::slotFullScreenMode(void)
{
  bool setVisible = false;
  static QSize m_size = size();

  if(ui.actionFull_Screen_Mode->text().contains("Full"))
    {
      setVisible = false;
      ui.actionFull_Screen_Mode->setText("&Normal Screen Mode");
    }
  else
    {
      setVisible = true;
      ui.actionFull_Screen_Mode->setText("&Full Screen Tablet Mode");
    }

  if(!setVisible)
    {
      m_size = size();
      showFullScreen();
    }
  else
    {
      showNormal();
      resize(m_size);
    }

  if(m_isJavaScriptWindow)
    ui.toolBar->setVisible(setVisible);
  else
    {
      menuBar()->setVisible(setVisible);

      if(qobject_cast<dview *> (ui.tabWidget->currentWidget()))
	statusBar()->setVisible(ui.actionStatusbar->isChecked());
      else
	statusBar()->setVisible(false);

      ui.toolBar->setVisible(setVisible);
    }

  ui.marker->setVisible(!setVisible);
  ui.tabWidget->setBarVisible
    (setVisible &&
     s_settings.value("settingsWindow/alwaysShowTabBar",
		      true).toBool());
  ui.restoreToolButton->setVisible(!setVisible);
  activateWindow();
}

void dooble::slotMarkerEntered(void)
{
  ui.marker->setVisible(false);
  ui.toolBar->setVisible(true);
  ui.tabWidget->setBarVisible(true);
  ui.restoreToolButton->setVisible(true);
}

void dooble::slotViewEntered(void)
{
  if(isFullScreen())
    {
      ui.toolBar->setVisible(false);
      ui.tabWidget->setBarVisible(false);
      ui.restoreToolButton->setVisible(false);
      ui.marker->setVisible(true);
    }
}

void dooble::slotSelectionChanged(const QString &text)
{
  Q_UNUSED(text);
}

void dooble::slotSelectionChanged(void)
{
}

void dooble::slotEnablePaste(void)
{
}

void dooble::slotIpAddressChanged(const QString &ipAddress)
{
  dview *p = qobject_cast<dview *> (sender());

  if(p && p == qobject_cast<dview *> (ui.tabWidget->currentWidget()))
    {
      if(p->title().isEmpty())
       {
         if(ipAddress.isEmpty() ||
            !s_settings.value("settingsWindow/displayIpAddress",
                              false).toBool())
           setWindowTitle(tr("Dooble Web Browser"));
         else
           setWindowTitle(QString(tr("Dooble Web Browser (%1)")).
                          arg(ipAddress));
       }
      else
       {
         if(ipAddress.isEmpty() ||
            !s_settings.value("settingsWindow/displayIpAddress",
                              false).toBool())
           setWindowTitle(p->title() + tr(" - Dooble Web Browser"));
         else
           setWindowTitle(p->title() + QString(" (%1)").arg(ipAddress) +
                          tr(" - Dooble Web Browser"));
       }
    }
}

void dooble::slotShowHistory(void)
{
  /*
  ** The current window should be the artificial parent.
  */

  disconnect(s_historyWindow, SIGNAL(open(const QUrl &)),
	     this, SLOT(slotLoadPage(const QUrl &)));
  disconnect(s_historyWindow, SIGNAL(createTab(const QUrl &)),
	     this, SLOT(slotOpenLinkInNewTab(const QUrl &)));
  disconnect(s_historyWindow, SIGNAL(openInNewWindow(const QUrl &)),
	     this, SLOT(slotOpenLinkInNewWindow(const QUrl &)));
  connect(s_historyWindow, SIGNAL(open(const QUrl &)),
	  this, SLOT(slotLoadPage(const QUrl &)));
  connect(s_historyWindow, SIGNAL(createTab(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewTab(const QUrl &)));
  connect(s_historyWindow, SIGNAL(openInNewWindow(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewWindow(const QUrl &)));
  s_historyWindow->show(this);
}

void dooble::slotShowBookmarks(void)
{
  /*
  ** The current window should be the artificial parent.
  */

  disconnect(s_bookmarksWindow, SIGNAL(open(const QUrl &)),
	     this, SLOT(slotLoadPage(const QUrl &)));
  disconnect(s_bookmarksWindow, SIGNAL(createTab(const QUrl &)),
	     this, SLOT(slotOpenLinkInNewTab(const QUrl &)));
  disconnect(s_bookmarksWindow, SIGNAL(openInNewWindow(const QUrl &)),
	     this, SLOT(slotOpenLinkInNewWindow(const QUrl &)));
  connect(s_bookmarksWindow, SIGNAL(open(const QUrl &)),
	  this, SLOT(slotLoadPage(const QUrl &)));
  connect(s_bookmarksWindow, SIGNAL(createTab(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewTab(const QUrl &)));
  connect(s_bookmarksWindow, SIGNAL(openInNewWindow(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewWindow(const QUrl &)));
  s_bookmarksWindow->show(this);
}

void dooble::slotResetUrl(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      QString title(p->title());

      if(title.isEmpty())
	/*
	** The tab's title will be set to the URL.
	*/

	title = p->url().toString(QUrl::StripTrailingSlash);

      if(title.isEmpty())
	title = tr("(Untitled)");

      ui.tabWidget->setTabText(ui.tabWidget->indexOf(p),
			       title.replace("&", "&&"));
      ui.tabWidget->setTabToolTip(ui.tabWidget->indexOf(p), title);
      ui.tabWidget->animateIndex
	(ui.tabWidget->indexOf(p), !p->isLoaded(), p->webviewIcon());
      ui.locationLineEdit->setText
	(p->url().toString(QUrl::StripTrailingSlash));
      ui.locationLineEdit->setBookmarkButtonEnabled
	(dmisc::isSchemeAcceptedByDooble(p->url().scheme()));
      ui.locationLineEdit->setIcon(p->icon());
      ui.locationLineEdit->setSecureColor(p->hasSecureConnection());
      ui.locationLineEdit->setBookmarkColor(p->isBookmarked());

      if(ui.bookmarksMenu->actions().size() > 0)
	ui.bookmarksMenu->actions().at(0)->setEnabled
	  (dmisc::isSchemeAcceptedByDooble(p->url().scheme()));

      slotOpenUrl();
    }
}

void dooble::slotCloseWindow(void)
{
  /*
  ** This method is connected to the windowCloseRequested() signal.
  */

  if(sender() && sender()->parent() && ui.tabWidget->count() > 1)
    {
      dview *p = qobject_cast<dview *> (sender()->parent()->parent());

      if(p)
	closeTab(ui.tabWidget->indexOf(p));
    }
  else
    close();
}

void dooble::launchDooble(const QUrl &url)
{
  QProcess *process = new QProcess();
  QStringList arguments;

  if(!url.isEmpty() && url.isValid())
    arguments << url.toString(QUrl::StripTrailingSlash);

  process->start(QCoreApplication::applicationDirPath() +
		 QDir::separator() + QCoreApplication::applicationName(),
		 arguments);
}

void dooble::slotTabMoved(int from, int to)
{
  if(from < 0 || to < 0)
    return;

  if(dmisc::passphraseWasAuthenticated() &&
     s_settings.value("settingsWindow/sessionRestoration", true).toBool())
    {
      dmisc::removeRestorationFiles(s_id, m_id);

      for(int i = 0; i < ui.tabWidget->count(); i++)
	{
	  dview *p = qobject_cast<dview *> (ui.tabWidget->widget(i));

	  if(p)
	    p->recordRestorationHistory();
	}
    }

  if(from < ui.menu_Tabs->actions().size() &&
     to < ui.menu_Tabs->actions().size())
    {
      QAction *toAction = ui.menu_Tabs->actions().at(to);
      QAction *fromAction = ui.menu_Tabs->actions().at(from);

      if(to < from)
	{
	  ui.menu_Tabs->removeAction(fromAction);
	  ui.menu_Tabs->insertAction(toAction, fromAction);
	}
      else
	{
	  ui.menu_Tabs->removeAction(toAction);
	  ui.menu_Tabs->insertAction(fromAction, toAction);
	}
    }
}

void dooble::slotGeometryChangeRequested(const QRect &geometry)
{
  if(geometry.isValid() && m_isJavaScriptWindow)
    if(s_settings.value("settingsWindow/javascriptEnabled",
			true).toBool() &&
       s_settings.value
       ("settingsWindow/javascriptAcceptGeometryChangeRequests",
	false).toBool())
      setGeometry(dmisc::balancedGeometry(geometry, this));
}

void dooble::slotHideMenuBar(void)
{
  if(m_isJavaScriptWindow)
    if(s_settings.value("settingsWindow/javascriptEnabled",
			true).toBool() &&
       s_settings.value("settingsWindow/javascriptAllowMenuBarHiding",
			true).toBool())
      {
	menuBar()->setVisible(false);
	ui.historyFrame->setVisible(false);
      }
}

void dooble::slotHideStatusBar(void)
{
  if(m_isJavaScriptWindow)
    if(s_settings.value("settingsWindow/javascriptEnabled",
			true).toBool() &&
       s_settings.value
       ("settingsWindow/javascriptAllowStatusBarHiding",
	false).toBool() &&
       !isFullScreen())
      statusBar()->setVisible(false);
}

void dooble::slotHideToolBar(void)
{
  if(m_isJavaScriptWindow)
    if(s_settings.value("settingsWindow/javascriptEnabled",
			true).toBool() &&
       s_settings.value("settingsWindow/javascriptAllowToolBarHiding",
			false).toBool())
      {
	ui.backToolButton->setVisible(false);
	ui.forwardToolButton->setVisible(false);
	ui.homeToolButton->setVisible(false);
	ui.reloadStopWidget->setVisible(false);
	ui.desktopToolButton->setVisible(false);
	ui.searchLineEdit->setVisible(false);
      }
}

void dooble::slotSetTabBarVisible(const bool state)
{
  if(ui.tabWidget->count() == 1)
    ui.tabWidget->setBarVisible(state);
}

void dooble::slotShowApplicationCookies(void)
{
  s_cookieWindow->show(this);
}

void dooble::prepareWidgetsBasedOnView(dview *p)
{
  if(!p)
    return;

  ui.zoomMenu->setEnabled(!p->isDir() && !p->isFtp());
  ui.actionFind->setEnabled(!p->isDir() && !p->isFtp());
  ui.actionSelect_All_Content->setEnabled(!p->isDir() && !p->isFtp());
  ui.actionShow_Hidden_Files->setEnabled(p->isDir());

  if(p->isDir() || p->isFtp())
    ui.findFrame->setVisible(false);
  else
    ui.findFrame->setVisible(showFindFrame);
}

void dooble::slotLocationSplitterMoved(int pos, int index)
{
  Q_UNUSED(pos);
  Q_UNUSED(index);

  if(!m_isJavaScriptWindow)
    {
      QSettings settings;

      settings.setValue("mainWindow/splitterState",
			ui.splitter->saveState());
      s_settings["mainWindow/splitterState"] =
	ui.splitter->saveState();
    }
}

void dooble::slotHistoryTabSplitterMoved(int pos, int index)
{
  Q_UNUSED(pos);
  Q_UNUSED(index);

  if(!m_isJavaScriptWindow)
    {
      QSettings settings;

      settings.setValue("mainWindow/historyTabSplitterState",
			ui.historyAndTabSplitter->saveState());
      s_settings["mainWindow/historyTabSplitterState"] =
	ui.historyAndTabSplitter->saveState();
    }
}

void dooble::setCurrentPage(dview *p)
{
  if(p)
    ui.tabWidget->setCurrentWidget(p);
}

bool dooble::promptForPassphrase(const bool override)
{
  /*
  ** This methods returns true if the user provided
  ** the correct passphrase.
  */

  if(dmisc::passphraseWasAuthenticated())
    return false;

  if((override || s_instances == 1) && dmisc::passphraseWasPrepared())
    {
      show();

      QDialog dialog(this);
      Ui_passphrasePrompt ui_p;

      ui_p.setupUi(&dialog);
#ifdef Q_WS_MAC
      dialog.setAttribute(Qt::WA_MacMetalStyle, false);
#endif
#ifdef Q_WS_WIN
      ui_p.passphraseLineEdit->setStyleSheet
	("QLineEdit { "
	 "padding: 1px; "
	 "border-style: solid; "
	 "border: 2px solid gray; "
	 "border-radius: 8px; "
	 "}");

      foreach(QPushButton *pushButton,
	      dialog.findChildren<QPushButton *> ())
	pushButton->setStyleSheet
	("QPushButton { "
	 "color: black; "
	 "background-color: "
	 "QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #eef, "
	 "stop: 1 #ccf); "
	 "border-width: 1px; "
	 "border-color: #339; "
	 "border-style: solid; "
	 "border-radius: 8px; "
	 "padding: 3px; "
	 "font-size: 12px; "
	 "padding-left: 5px; "
	 "padding-right: 5px; "
	 "min-width: 50px; "
	 "max-width: 70px; "
	 "min-height: 16px; "
	 "max-height: 16px; "
	 "}");
#endif

      QPixmap pixmap;
      QSettings settings(s_settings.value("iconSet").toString(),
			 QSettings::IniFormat);

      pixmap.load(settings.value("settingsWindow/safeButtonIcon").
		  toString());
      ui_p.label->setPixmap(pixmap.scaled(QSize(48, 48),
					  Qt::KeepAspectRatio,
					  Qt::SmoothTransformation));
      dialog.setWindowIcon
	(QIcon(settings.value("mainWindow/windowIcon").toString()));

      for(int i = 0; i < ui_p.buttonBox->buttons().size(); i++)
	if(ui_p.buttonBox->buttonRole(ui_p.buttonBox->buttons().at(i)) ==
	   QDialogButtonBox::AcceptRole)
	  {
	    if(qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().
					    at(i)))
	      qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().
					   at(i))->setDefault(true);

	    ui_p.buttonBox->buttons().at(i)->setIcon
	      (QIcon(settings.value("okButtonIcon").toString()));
	    ui_p.buttonBox->buttons().at(i)->setIconSize(QSize(16, 16));
	  }
	else
	  {
	    if(qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().
					    at(i)))
	      qobject_cast<QPushButton *> (ui_p.buttonBox->buttons().
					   at(i))->setDefault(false);

	    ui_p.buttonBox->buttons().at(i)->setIcon
	      (QIcon(settings.value("cancelButtonIcon").toString()));
	    ui_p.buttonBox->buttons().at(i)->setIconSize(QSize(16, 16));
	  }

      if(dialog.exec() == QDialog::Accepted)
	{
	  QString hash("");
	  QString salt(s_settings.
		       value("settingsWindow/passphraseSalt", "").toString());
	  QString hashType(s_settings.
			   value("settingsWindow/passphraseHashType", "").
			   toString());

	  /*
	  ** Validate the passphrase.
	  */

	  hash = dmisc::passphraseHash
	    (ui_p.passphraseLineEdit->text(), salt, hashType);

	  if(hash == s_settings.value("settingsWindow/passphraseHash").
	     toString())
	    {
	      QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
	      dmisc::setCipherPassphrase
		(ui_p.passphraseLineEdit->text(), false);
	      QApplication::restoreOverrideCursor();
	      ui_p.passphraseLineEdit->clear();
	      emit passphraseWasAuthenticated
		(dmisc::passphraseWasAuthenticated());

	      foreach(QWidget *widget, QApplication::topLevelWidgets())
		if(qobject_cast<dooble *> (widget))
		  {
		    dooble *d = qobject_cast<dooble *> (widget);

		    if(d)
		      d->ui.action_Authenticate->setEnabled
			(!dmisc::passphraseWasAuthenticated());
		  }

	      s_makeCrashFile();
	      return true;
	    }
	  else
	    {
	      ui_p.passphraseLineEdit->clear();
	      return promptForPassphrase(override);
	    }
	}
      else
	{
	  ui_p.passphraseLineEdit->clear();

	  /*
	  ** Create a random super-secret key.
	  */

	  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
	  dmisc::setCipherPassphrase(QString(), false);
	  QApplication::restoreOverrideCursor();
	}
    }
  else if(!override && s_instances == 1)
    {
      QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
      dmisc::setCipherPassphrase(QString(), false);
      QApplication::restoreOverrideCursor();
    }

  return false;
}

void dooble::initializeHistory(void)
{
  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

  /*
  ** Add history menu actions, but only if the user wishes
  ** to have a history. The entries will be date-ordered.
  */

  if(s_settings.value("settingsWindow/rememberHistory",
		      true).toBool())
    {
      {
	QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "history");

	db.setDatabaseName(s_homePath +
			   QDir::separator() + "history.db");

	if(db.open())
	  {
	    int temporary = dmisc::passphraseWasAuthenticated() ? 0 : 1;
	    QSqlQuery query(db);
	    QPair<QString, QIcon> pair;
	    QMultiMap<QDateTime, QVariantList> items;

	    if(query.exec(QString("SELECT url, title, icon, "
				  "last_visited, visits "
				  "FROM history WHERE "
				  "temporary = %1").arg(temporary)))
	      while(query.next())
		{
		  QUrl url
		    (QUrl::fromEncoded
		     (dmisc::decodedString
		      (QByteArray::fromBase64
		       (query.value(0).toByteArray())),
		      QUrl::StrictMode));

		  /*
		  ** Simple test to determine if the URL
		  ** can be decoded. If it can't be,
		  ** ignore the history entry.
		  */

		  if(!url.isValid())
		    {
		      QSqlQuery deleteQuery(db);

		      deleteQuery.exec
			("PRAGMA synchronous = OFF");
		      deleteQuery.prepare("DELETE FROM history WHERE "
					  "url = ? AND temporary = ?");
		      deleteQuery.bindValue(0, query.value(0));
		      deleteQuery.bindValue(1, temporary);
		      deleteQuery.exec();
		      continue;
		    }

		  if(!dmisc::isSchemeAcceptedByDooble(url.scheme()))
		    continue;

		  if(ui.historyMenu->actions().size() == 4)
		    {
		      ui.historyMenu->actions().at(0)->setEnabled(true);
		      ui.historyMenu->actions().at(1)->setEnabled(true);
		      ui.historyMenu->addSeparator();
		    }

		  QIcon icon;

		  if(!query.isNull(2))
		    {
		      QBuffer buffer;
		      QByteArray bytes
			(QByteArray::fromBase64(query.value(2).toByteArray()));

		      bytes = dmisc::decodedString(bytes);
		      buffer.setBuffer(&bytes);
		      buffer.open(QIODevice::ReadOnly);

		      QDataStream in(&buffer);

		      in >> icon;
		      buffer.close();
		    }

		  if(icon.isNull())
		    icon = dmisc::iconForUrl(url);

		  QString title("");
		  QDateTime dateTime
		    (QDateTime::fromString
		     (QString::fromUtf8
		      (dmisc::decodedString
		       (QByteArray::fromBase64
			(query.value(3).toByteArray()))),
		      Qt::ISODate));

		  title = QString::fromUtf8
		    (dmisc::decodedString
		     (QByteArray::fromBase64
		      (query.value(1).toByteArray())));

		  if(title.isEmpty())
		    title = url.toString(QUrl::StripTrailingSlash);

		  title = dmisc::elidedTitleText(title);

		  if(title.isEmpty())
		    title = tr("Dooble Web Browser");

		  QVariantList list;

		  list.append(url);
		  list.append(icon);
		  list.append(title);
		  items.insert(dateTime, list);

		  QString host(url.host());
		  QString visits
		    (dmisc::decodedString
		     (QByteArray::fromBase64
		      (query.value(4).toByteArray())));

		  if(!host.isEmpty())
		    s_mostVisitedHosts[host] += visits.toInt();
		}

	    QMapIterator<QDateTime, QVariantList> i(items);

	    i.toBack();

	    while(i.hasPrevious())
	      {
		i.previous();

		if(i.value().size() < 3)
		  continue;

		QIcon icon(i.value().at(1).value<QIcon> ());

		if(icon.isNull())
		  icon = dmisc::iconForUrl(i.value().at(0).toUrl());

		QAction *action = new QAction(icon,
					      i.value().at(2).toString(),
					      this);

		action->setData(i.value().at(0));
		connect(action, SIGNAL(triggered(void)), this,
			SLOT(slotLinkActionTriggered(void)));
		ui.historyMenu->addAction(action);

		if(ui.historyMenu->actions().size() >= 5 + MAX_HISTORY_ITEMS)
		  break;
	      }

	    /*
	    ** The history menu should have at least three entries
	    ** at this point.
	    */

	    ui.locationLineEdit->appendItems(items);
	  }

	db.close();
      }

      QSqlDatabase::removeDatabase("history");
    }

  QApplication::restoreOverrideCursor();
}

void dooble::initializeBookmarksMenu(void)
{
  ui.bookmarksMenu->clear();
  ui.bookmarksMenu->addActions(s_bookmarksWindow->actions(this));

  if(ui.bookmarksMenu->actions().size() >= 3)
    {
      ui.bookmarksMenu->actions().at(0)->setEnabled
	(ui.locationLineEdit->isBookmarkeButtonEnabled());

      QSettings settings(s_settings.value("iconSet").toString(),
			 QSettings::IniFormat);

      ui.bookmarksMenu->actions().at(0)->setIcon
	(QIcon(settings.value("mainWindow/actionBookmarks").toString()));
      ui.bookmarksMenu->actions().at(2)->setIcon
	(QIcon(settings.value("mainWindow/actionBookmarks").toString()));
      connect(ui.bookmarksMenu->actions().at(0),
	      SIGNAL(triggered(void)),
	      this,
	      SLOT(slotBookmark(void)));
      connect(ui.bookmarksMenu->actions().at(2),
	      SIGNAL(triggered(void)),
	      this,
	      SLOT(slotShowBookmarks(void)));
    }
}

void dooble::slotOpenP2PEmail(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p && p->isModified() && !warnAboutLeavingModifiedTab())
    return;

  QUrl url(QUrl::fromUserInput(s_settings.value("settingsWindow/p2pUrl",
						"about: blank").toString()));

  url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));
  loadPage(url);

  if(ui.tabWidget->currentWidget())
    ui.tabWidget->currentWidget()->setFocus();
}

void dooble::slotOpenIrcChannel(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p && p->isModified() && !warnAboutLeavingModifiedTab())
    return;

  QUrl url(QUrl::fromUserInput("http://webchat.freenode.net?channels=dooble"));

  url = QUrl::fromEncoded(url.toEncoded(QUrl::StripTrailingSlash));
  loadPage(url);

  if(ui.tabWidget->currentWidget())
    ui.tabWidget->currentWidget()->setFocus();
}

void dooble::slotRepaintRequested(const QRect &dirtyRect)
{
  dwebpage *p = qobject_cast<dwebpage *> (sender());

  if(p && p->mainFrame())
    p->mainFrame()->render(0, dirtyRect);
}

void dooble::slotShowFavoritesToolBar(bool checked)
{
  ui.favoritesToolBar->blockSignals(true);
  ui.favoritesToolBar->setVisible(checked);
  ui.favoritesToolBar->blockSignals(false);

  QSettings settings;

  settings.setValue("mainWindow/showFavoritesToolBar", checked);
  s_settings["mainWindow/showFavoritesToolBar"] = checked;

  if(checked)
    prepareMostVisited();
}

void dooble::prepareMostVisited(void)
{
  if(!ui.favoritesToolBar->isVisible())
    return;

  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

  foreach(QToolButton *toolButton,
	  ui.favoritesToolButtonsFrame->findChildren<QToolButton *> ())
    delete toolButton;

  QList<int> list(s_mostVisitedHosts.values());
  QStringList addedHosts;

  qSort(list);

  for(int i = list.size() - 1, j = 0; i >= 0; i--)
    {
      QList<QString> keys(s_mostVisitedHosts.keys(list.at(i)));

      /*
      ** We should have something like:
      ** (Count (C1 >= C2 >= ... >= CN): Host
      ** (H1 >= H2 >= ... >= HI >= ... >= HJ))
      ** C1: H1
      ** C1: H2
      ** ...
      ** C1: HI
      ** ...
      ** CN: HJ
      */

      qSort(keys);

      /*
      ** Multiple hosts may enjoy identical counts.
      */

      for(int k = 0; k < keys.size(); k++)
	if(j < MAX_MOST_VISITED_ITEMS)
	  {
	    if(!keys.at(k).isEmpty() &&
	       !addedHosts.contains(keys.at(k)))
	      {
		QUrl url(QUrl::fromUserInput(keys.at(k)));
		QIcon icon(dmisc::iconForUrl(url));
		QToolButton *toolButton = new QToolButton
		  (ui.favoritesToolButtonsFrame);

		toolButton->setIcon(icon);
		toolButton->setToolTip
		  (url.toString(QUrl::StripTrailingSlash));
		toolButton->setIconSize(QSize(16, 16));
#ifdef Q_WS_MAC
		toolButton->setStyleSheet
		  ("QToolButton {border: none;}"
		   "QToolButton::menu-button {border: none;}");
#else
		toolButton->setAutoRaise(true);
#endif
		ui.favoritesToolButtonsFrame->layout()->
		  addWidget(toolButton);
		toolButton->setProperty("url", url);
		connect(toolButton,
			SIGNAL(clicked(void)),
			this,
			SLOT(slotFavoritesToolButtonClicked(void)));
		j += 1;
		addedHosts.append(keys.at(k));
	      }
	  }
	else
	  break;

      if(j >= MAX_MOST_VISITED_ITEMS)
	break;
    }

  QApplication::restoreOverrideCursor();
}

void dooble::slotFavoritesToolButtonClicked(void)
{
  QToolButton *toolButton = qobject_cast<QToolButton *> (sender());

  if(toolButton)
    {
      dview *p = newTab(toolButton->property("url").toUrl());

      if(p)
	{
	  ui.tabWidget->setCurrentWidget(p);
	  ui.tabWidget->update();
	}
    }
}

void dooble::slotShowIpAddress(const bool state)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(!p)
    return;

  if(state)
    p->findIpAddress(p->url());
  else
    {
      if(p->title().isEmpty())
	setWindowTitle(tr("Dooble Web Browser"));
      else
	setWindowTitle(p->title() + tr(" - Dooble Web Browser"));
    }
}

void dooble::slotShowHistorySideBar(bool state)
{
  ui.historyFrame->setVisible(state);
  dooble::s_settings["mainWindow/showHistorySideBar"] = state;

  QSettings settings;

  settings.setValue("mainWindow/showHistorySideBar", state);
}

void dooble::slotHistorySideBarClosed(void)
{
  ui.actionShow_HistorySideBar->setChecked(false);
  slotShowHistorySideBar(false);
}

void dooble::slotOpenUrlsFromDrop(const QList<QUrl> &list)
{
  if(list.size() > 5)
    {
      QApplication::restoreOverrideCursor();

      QMessageBox mb(this);

#ifdef Q_WS_MAC
      mb.setAttribute(Qt::WA_MacMetalStyle, false);
#endif
      mb.setIcon(QMessageBox::Question);
      mb.setWindowTitle(tr("Dooble Web Browser: Confirmation"));
      mb.setWindowModality(Qt::WindowModal);
      mb.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
      mb.setText(QString(tr("Are you sure that you wish to open "
			    "%1 pages?")).arg(list.size()));

      QSettings settings(s_settings.value("iconSet").toString(),
			 QSettings::IniFormat);

      for(int i = 0; i < mb.buttons().size(); i++)
	if(mb.buttonRole(mb.buttons().at(i)) == QMessageBox::YesRole)
	  {
	    mb.buttons().at(i)->setIcon
	      (QIcon(settings.value("okButtonIcon").toString()));
	    mb.buttons().at(i)->setIconSize(QSize(16, 16));
	  }
	else
	  {
	    mb.buttons().at(i)->setIcon
	      (QIcon(settings.value("cancelButtonIcon").toString()));
	    mb.buttons().at(i)->setIconSize(QSize(16, 16));
	  }

      mb.setWindowIcon
	(QIcon(settings.value("mainWindow/windowIcon").toString()));

      if(mb.exec() == QMessageBox::No)
	return;
    }

  for(int i = 0; i < list.size(); i++)
    if(i == 0)
      {
	dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

	if(p && p->isModified() && !warnAboutLeavingModifiedTab())
	  return;

	loadPage(list.at(i));
      }
    else
      newTab(list.at(i));

  ui.tabWidget->update();

  if(ui.tabWidget->currentWidget())
    ui.tabWidget->currentWidget()->setFocus();
}

void dooble::copyDooble(dooble *d)
{
  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

  if(d)
    {
      for(int i = 0; i < d->ui.historyMenu->actions().size(); i++)
	if(i == 0 || i == 1)
	  ui.historyMenu->actions().at(i)->setEnabled
	    (d->ui.historyMenu->actions().at(i)->isEnabled());
	else if(i == 2)
	  ui.historyMenu->addSeparator();
	else if(i == 3 || i == 4)
	  continue;
	else
	  {
	    QAction *a = new QAction
	      (d->ui.historyMenu->actions().at(i)->icon(),
	       d->ui.historyMenu->actions().at(i)->text(),
	       this);

	    a->setData(d->ui.historyMenu->actions().at(i)->data());
	    connect(a, SIGNAL(triggered(void)), this,
		    SLOT(slotLinkActionTriggered(void)));
	    ui.historyMenu->addAction(a);
	  }

      ui.locationLineEdit->copyContentsOf(d->ui.locationLineEdit);
    }
  else
    initializeHistory();

  QApplication::restoreOverrideCursor();
}

void dooble::slotStatusBarMessage(const QString &text)
{
  /*
  ** As of version 1.32, this method is not connected to the
  ** statusBarMessage() signal. If the signal is connected to
  ** this method, dfilemanager and dftpbrowser must be considered.
  */

  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());
  dwebpage *d = qobject_cast<dwebpage *> (sender());

  if(d && p && d == p->page())
    {
      sb.statusLabel->setText(text);
      QTimer::singleShot(1000, sb.statusLabel, SLOT(clear(void)));
    }
}

void dooble::slotObjectDestroyed(QObject *object)
{
  QWidget *w = qobject_cast<QWidget *> (object);

  if(w)
    {
      if(ui.tabWidget->indexOf(w) != -1)
	ui.tabWidget->removeTab(ui.tabWidget->indexOf(w));

      if(ui.tabWidget->count() == 1)
	{
	  ui.tabWidget->setTabsClosable(false);
	  ui.tabWidget->setBarVisible
	    (s_settings.value("settingsWindow/alwaysShowTabBar",
			      true).toBool());
	}
    }
}

bool dooble::warnAboutLeavingModifiedTab(void)
{
  /*
  ** Returns true if the warning was accepted or
  ** if the setting is disabled.
  */

  if(s_settings.value("settingsWindow/warnBeforeLeavingModifiedTab",
		      false).toBool())
    {
      QMessageBox mb(this);

#ifdef Q_WS_MAC
      mb.setAttribute(Qt::WA_MacMetalStyle, false);
#endif
      mb.setIcon(QMessageBox::Question);
      mb.setWindowTitle(tr("Dooble Web Browser: Confirmation"));
      mb.setWindowModality(Qt::WindowModal);
      mb.setStandardButtons(QMessageBox::Cancel |
			    QMessageBox::No | QMessageBox::Yes);
      mb.setText(tr("Are you sure that you wish to leave the modified "
		    "page?"));

      QSettings settings(s_settings.value("iconSet").toString(),
			 QSettings::IniFormat);

      for(int i = 0; i < mb.buttons().size(); i++)
	if(mb.buttonRole(mb.buttons().at(i)) == QMessageBox::YesRole)
	  {
	    mb.buttons().at(i)->setIcon
	      (QIcon(settings.value("okButtonIcon").toString()));
	    mb.buttons().at(i)->setIconSize(QSize(16, 16));
	  }
	else
	  {
	    mb.buttons().at(i)->setIcon
	      (QIcon(settings.value("cancelButtonIcon").toString()));
	    mb.buttons().at(i)->setIconSize(QSize(16, 16));
	  }

      mb.setWindowIcon
	(QIcon(settings.value("mainWindow/windowIcon").toString()));

      if(mb.exec() == QMessageBox::Yes)
	return true;
      else
	return false;
    }

  return true;
}

void dooble::slotAuthenticate(void)
{
  if(promptForPassphrase(true))
    {
      clearHistoryWidgets();
      initializeHistory();

      /*
      ** We're not going to populate the History model and window.
      */

      QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
      s_cookies->populate();
      s_cookieWindow->populate();
      s_downloadWindow->populate();
      s_bookmarksWindow->populate();
      s_popupsWindow->populate();
      s_adBlockWindow->populate();
      s_cookiesBlockWindow->populate();
      s_httpRedirectWindow->populate();
      s_httpReferrerWindow->populate();
      s_javaScriptExceptionsWindow->populate();
      s_dntWindow->populate();
      s_imageBlockWindow->populate();
      s_cacheExceptionsWindow->populate();
      s_networkCache->populate();
      QApplication::restoreOverrideCursor();

      foreach(QWidget *widget, QApplication::topLevelWidgets())
	if(qobject_cast<dooble *> (widget))
	  {
	    dooble *d = qobject_cast<dooble *> (widget);

	    if(d)
	      {
		d->prepareMostVisited();

		/*
		** Copy this window's important attributes.
		*/

		if(d != this)
		  d->copyDooble(this);
	      }
	  }
    }
}

void dooble::setUrlHandler(dooble *d)
{
  QDesktopServices::setUrlHandler("file",
				  d,
				  "slotOpenLinkInNewTab");
  QDesktopServices::setUrlHandler("ftp",
				  d,
				  "slotOpenLinkInNewTab");
  QDesktopServices::setUrlHandler("http",
				  d,
				  "slotOpenLinkInNewTab");
  QDesktopServices::setUrlHandler("https",
				  d,
				  "slotOpenLinkInNewTab");
  QDesktopServices::setUrlHandler("qrc",
				  d,
				  "slotOpenLinkInNewTab");
}

void dooble::unsetUrlHandler(void)
{
  QDesktopServices::unsetUrlHandler("file");
  QDesktopServices::unsetUrlHandler("ftp");
  QDesktopServices::unsetUrlHandler("http");
  QDesktopServices::unsetUrlHandler("https");
  QDesktopServices::unsetUrlHandler("qrc");

  foreach(QWidget *widget, QApplication::topLevelWidgets())
    if(qobject_cast<dooble *> (widget))
      {
	dooble *d = qobject_cast<dooble *> (widget);

	if(d && d != this)
	  {
	    setUrlHandler(d);
	    break;
	  }
      }
}

void dooble::slotReopenClosedTab(void)
{
  QAction *action = qobject_cast<QAction *> (sender());

  if(action)
    {
      int index = -1;

      /*
      ** First, delete the correct action from the Recently-Closed Tabs
      ** menu.
      */

      if(ui.historyMenu->actions().size() >= 4)
	if(ui.historyMenu->actions().at(3)->menu())
	  for(int i = 0;
	      i < ui.historyMenu->actions().at(3)->menu()->actions().size();
	      i++)
	    if(action == ui.historyMenu->actions().at(3)->menu()->
	       actions().at(i))
	      {
		index = i;
		action->deleteLater();
		break;
	      }

      if(index > -1)
	/*
	** Subtract 2 (Clear action and separator)
	*/

	index -= 2;

      /*
      ** Create a view having the old view's history.
      */

      for(int i = 0; i < m_closedTabs.count(); i++)
	if(i == index)
	  {
	    dview *p = 0;
	    QByteArray history = m_closedTabs.at(i);

	    if((p = newTab(history)))
	      {
		ui.tabWidget->setCurrentWidget(p);
		ui.tabWidget->update();
	      }

	    m_closedTabs.removeAt(i);
	    prepareTabsMenu();
	    break;
	  }

      if(m_closedTabs.isEmpty() &&
	 ui.historyMenu->actions().size() >= 4)
	{
	  ui.historyMenu->actions().at(3)->setEnabled(false);

	  if(ui.historyMenu->actions().at(3)->menu())
	    {
	      while(!ui.historyMenu->actions().at(3)->menu()->actions().
		    isEmpty())
		{
		  QAction *action = ui.historyMenu->actions().at(3)->menu()->
		    actions().last();

		  ui.historyMenu->actions().at(3)->menu()->
		    removeAction(action);
		  action->deleteLater();
		}

	      ui.historyMenu->actions().at(3)->menu()->deleteLater();
#ifdef Q_WS_MAC
	      ui.historyMenu->actions().at(3)->setMenu(0);
#endif
	    }
	}
    }
}

void dooble::prepareTabsMenu(void)
{
  ui.menu_Tabs->clear();

  for(int i = 0; i < ui.tabWidget->count(); i++)
    {
      dview *p = qobject_cast<dview *> (ui.tabWidget->widget(i));
      QAction *action = 0;

      if(p)
	action = p->tabAction();
      else
	{
	  dreinstatedooble *reinstateWidget =
	    qobject_cast<dreinstatedooble *> (ui.tabWidget->widget(i));

	  if(reinstateWidget)
	    action = reinstateWidget->tabAction();
	  else
	    {
	      dplugintab *plugTab = qobject_cast<dplugintab *>
		(ui.tabWidget->widget(i));

	      if(plugTab)
		action = plugTab->tabAction();
	      else if(m_desktopWidget)
		action = m_desktopWidget->tabAction();
	    }
	}

      if(action)
	ui.menu_Tabs->addAction(action);
    }

  ui.menu_Tabs->setEnabled(!ui.menu_Tabs->actions().isEmpty());
}

QString dooble::pluginPath(void)
{
  return QCoreApplication::applicationDirPath() + "/Plugins";
}

void dooble::slotRefreshPlugins(void)
{
  QList<QAction *> actions = ui.menu_Plugins->actions();

  for(int a = 0; a < actions.count(); a++)
    {
      if(actions.at(a)->objectName() != "actionRefreshPlugins" &&
	 !actions.at(a)->isSeparator())
        {
          QAction *dead = actions.at(a);
          std::cerr << "Removing action " << dead->objectName().toStdString()
                    << "." << std::endl;
          ui.menu_Plugins->removeAction(dead);
          dead->deleteLater();
        }
    }

  QDir plugDir(pluginPath());
  QStringList fileList(plugDir.entryList(QDir::Files |
					 QDir::Readable |
					 QDir::NoDotAndDotDot,
					 QDir::IgnoreCase));

  for(int p = 0; p < fileList.count(); p++)
    {
      QString fileName(fileList.at(p));

      if(!pluginLoadCount.contains(fileName))
        {
          pluginLoadCount[fileName] = 0;
        }

      if(pluginLoadCount[fileName] < 1 &&
	 fileName != "README")
        {
          addPluginAction(fileName);
        }
    }
}

void dooble::addPluginAction(const QString &pluginFileName)
{
  QAction *plugAction = new QAction(this);
  QStringList ops("start");

  ops.append(pluginFileName);
  plugAction->setData(ops);

  QString title(pluginFileName);

  if(title.startsWith("lib"))
    {
      title.remove(0, 3);
    }

  title = title.section(".", 0, 0);

  if(!title.isEmpty())
    title[0] = title.at(0).toUpper();

  plugAction->setText(tr("Start %1").arg(title));
  ui.menu_Plugins->addAction(plugAction);
}

void dooble::slotPluginAction(QAction *plugAction)
{
  if(!plugAction)
    {
      return;
    }

  QVariant plugData = plugAction->data();

  if(plugData.type() != QVariant::StringList)
    {
      return;
    }

  QStringList ops = plugData.toStringList();

  if(ops.count() != 2)
    {
      return;
    }

  if(ops.at(0) == "start")
    {
      startPlugin(ops.at(1));
    }
}

Extension *dooble::loadPlugin(const QString &plugName)
{
  QPluginLoader loader;
  QString filename(plugName);
  Extension *loadedPlugin(0);
  QString fullName(pluginPath() + QString("/") + filename);

  loader.setFileName(fullName);

  bool ok = loader.load();

  if(ok)
    {
      loadedPlugin = qobject_cast<Extension *> (loader.instance());
    }
  else
    {
      QMessageBox::information(this, tr("Add-on Load Failed"),
			       loader.errorString());
    }

  return loadedPlugin;
}

void dooble::startPlugin(const QString &plugName)
{
  Extension *plugin = loadPlugin(plugName);

  if(plugin)
    {
      dplugintab *plugTab = new dplugintab(plugin, this);

      pluginLoadCount[plugName] += 1;
      pluginMap[plugin->widget()] = PluginRec(plugTab, plugName);

      if(plugTab)
        {
	  QIcon icon(plugTab->icon());

	  if(icon.isNull())
	    icon = dmisc::iconForUrl(QUrl());

	  QString title(tr("Add-on"));

          ui.tabWidget->addTab(plugTab, icon, title);
	  ui.tabWidget->setTabsClosable(ui.tabWidget->count() > 1);
          ui.tabWidget->setCurrentWidget(plugTab);
	  ui.tabWidget->setTabToolTip(ui.tabWidget->currentIndex(), title);

	  QAction *action = new QAction(icon, title, this);

	  action->setData(title);
	  connect(action, SIGNAL(triggered(void)), this,
		  SLOT(slotShowAddOns(void)));
	  plugTab->setTabAction(action);
	  prepareTabsMenu();

          SignalAgent *agent = plugin->agent();

          if(agent)
            {
              connect(agent, SIGNAL(exiting(Extension *, int)),
		      this, SLOT(slotPluginExiting(Extension *, int)));
              connect(agent, SIGNAL(iconChange(Extension *)),
		      plugTab, SLOT(slotIconChange(Extension *)));
              connect(agent, SIGNAL(titleChange(Extension *)),
		      plugTab, SLOT(slotTitleChange(Extension *)));
              connect(plugTab, SIGNAL(iconChange(QWidget *, const QIcon &)),
		      ui.tabWidget,
		      SLOT(slotIconChange(QWidget *, const QIcon &)));
              connect(plugTab,
		      SIGNAL(titleChange(QWidget *, const QString &)),
		      ui.tabWidget,
		      SLOT(slotTitleChange(QWidget *, const QString &)));
            }
        }

      slotRefreshPlugins();
      plugin->run();

      /*
      ** After a plugin is started, we need to update icons and titles.
      ** The plugin should emit some signals when their icons and titles
      ** change. The following may not produce the intended results.
      */

      if(plugTab)
	{
	  QIcon icon(plugTab->icon());

	  if(icon.isNull())
	    icon = dmisc::iconForUrl(QUrl());

	  QString title(tr("Add-on"));

          ui.tabWidget->setTabIcon(ui.tabWidget->indexOf(plugTab), icon);
	  ui.tabWidget->setTabText(ui.tabWidget->indexOf(plugTab), title);
	  ui.tabWidget->setTabToolTip(ui.tabWidget->indexOf(plugTab), title);

	  QAction *action = plugTab->tabAction();

	  if(action)
	    action->setData(title);

	  prepareTabsMenu();
	}
    }
}

void dooble::slotPluginExiting(Extension *plugin, int status)
{
  Q_UNUSED(status);

  if(plugin)
    {
      QWidget *plugWidget = plugin->widget();
      dplugintab *plugTab = pluginMap[plugWidget].tab;

      pluginLoadCount[pluginMap[plugWidget].fileName] -= 1;

      if(plugTab)
        {
	  pluginMap.remove(plugWidget);

	  int index = ui.tabWidget->indexOf(plugTab);

	  if(index >= 0)
	    {
	      ui.tabWidget->removeTab(index);
	    }
        }

      slotRefreshPlugins();
      prepareTabsMenu();
    }
}

void dooble::slotClearRecentlyClosedTabs(void)
{
  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
  m_closedTabs.clear();

  if(ui.historyMenu->actions().size() >= 4 &&
     ui.historyMenu->actions().at(3)->menu())
    {
      while(!ui.historyMenu->actions().at(3)->menu()->actions().
	    isEmpty())
	{
	  QAction *action = ui.historyMenu->actions().at(3)->menu()->
	    actions().last();

	  ui.historyMenu->actions().at(3)->menu()->removeAction(action);
	  action->deleteLater();
	}

      ui.historyMenu->actions().at(3)->menu()->deleteLater();
#ifdef Q_WS_MAC
      ui.historyMenu->actions().at(3)->setMenu(0);
#endif
    }

  if(ui.historyMenu->actions().size() >= 4)
    ui.historyMenu->actions().at(3)->setEnabled(false);

  QApplication::restoreOverrideCursor();
}

void dooble::slotExceptionRaised(dexceptionswindow *window,
				 const QUrl &url)
{
  if(!s_settings.value("settingsWindow/notifyOfExceptions", true).toBool())
    return;

  if(window && !window->contains(url.host()))
    {
      if(url.isEmpty() || !url.isValid())
	sb.exceptionsToolButton->setToolTip
	  (tr("An unknown (empty or invalid URL) site caused an exception. "
	      "Please click to review."));
      else if(url.host().isEmpty())
	sb.exceptionsToolButton->setToolTip
	  (QString(tr("The site %1 caused an exception. "
		      "Please click to review.").
		   arg(url.toString(QUrl::StripTrailingSlash))));
      else
	sb.exceptionsToolButton->setToolTip
	  (QString(tr("The site %1 caused an exception. "
		      "Please click to review.").
		   arg(url.host())));

      sb.exceptionsToolButton->setVisible(true);
      sb.exceptionsToolButton->disconnect(window);
      connect(sb.exceptionsToolButton,
	      SIGNAL(clicked(void)),
	      window,
	      SLOT(slotShow(void)));
      connect(sb.exceptionsToolButton,
	      SIGNAL(clicked(void)),
	      sb.exceptionsToolButton,
	      SLOT(hide(void)));
    }
}

void dooble::slotClearContainers(void)
{
  s_clearContainersWindow->show(this);
}

void dooble::slotClearHistory(void)
{
  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
  s_mostVisitedHosts.clear();

  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "history");

    db.setDatabaseName(s_homePath +
		       QDir::separator() + "history.db");

    if(db.open())
      {
	int temporary = dmisc::passphraseWasAuthenticated() ? 0 : 1;
	QSqlQuery query(db);

	query.exec("PRAGMA synchronous = OFF");

	if(!temporary)
	  query.exec("DELETE FROM history");
	else
	  query.exec("DELETE FROM history WHERE temporary = 1");

	query.exec("VACUUM");
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("history");
  QApplication::restoreOverrideCursor();
  clearHistoryWidgets();
}

void dooble::clearHistoryWidgets(void)
{
  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

  /*
  ** Clears every window's history widgets, including recently-closed tabs.
  */

  foreach(QWidget *widget, QApplication::topLevelWidgets())
    {
      dooble *d = qobject_cast<dooble *> (widget);

      if(!d)
	continue;

      emit d->clearHistory();
      d->ui.locationLineEdit->clearHistory();
      d->m_closedTabs.clear();

      if(d->ui.historyMenu->actions().size() >= 4 &&
	 d->ui.historyMenu->actions().at(3)->menu())
	{
	  while(!d->ui.historyMenu->actions().at(3)->menu()->actions().
		isEmpty())
	    {
	      QAction *action = d->ui.historyMenu->actions().at(3)->menu()->
		actions().last();

	      d->ui.historyMenu->actions().at(3)->menu()->removeAction(action);
	      action->deleteLater();
	    }

	  d->ui.historyMenu->actions().at(3)->menu()->deleteLater();
#ifdef Q_WS_MAC
	  d->ui.historyMenu->actions().at(3)->setMenu(0);
#endif
	}

      while(d->ui.historyMenu->actions().size() > 4)
	{
	  QAction *action = d->ui.historyMenu->actions().last();

	  d->ui.historyMenu->removeAction(action);
	  action->deleteLater();
	}

      if(d->ui.historyMenu->actions().size() >= 4)
	{
	  d->ui.historyMenu->actions().at(0)->setEnabled(false);
	  d->ui.historyMenu->actions().at(1)->setEnabled(true);
	  d->ui.historyMenu->actions().at(3)->setEnabled(false);
	}

      foreach(QToolButton *toolButton,
	      d->ui.favoritesToolButtonsFrame->findChildren<QToolButton *> ())
	toolButton->deleteLater();

      d->ui.backToolButton->menu()->clear();
      d->ui.forwardToolButton->menu()->clear();
      d->ui.backToolButton->setEnabled(false);
      d->ui.forwardToolButton->setEnabled(false);
    }

  QApplication::restoreOverrideCursor();
}

void dooble::slotBookmark(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    {
      if(p->isBookmarked())
	{
	  s_bookmarksPopup->populate(p->url());
	  s_bookmarksPopupMenu->exec
	    (ui.locationLineEdit->mapToGlobal(ui.locationLineEdit->
					      bookmarkButtonPopupPosition()));
	}
      else
	{
	  QDateTime now(QDateTime::currentDateTime());

	  s_bookmarksWindow->slotAddBookmark(p->webviewUrl(),
					     p->webviewIcon(),
					     p->title(),
					     p->description(),
					     now,
					     now);
	}
    }
}

void dooble::slotBookmark(const int index)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->widget(index));

  if(p)
    {
      if(p->isBookmarked())
	{
	  s_bookmarksPopup->populate(p->url());
	  s_bookmarksPopupMenu->exec
	    (ui.locationLineEdit->mapToGlobal(ui.locationLineEdit->
					      bookmarkButtonPopupPosition()));
	}
      else
	{
	  QDateTime now(QDateTime::currentDateTime());

	  s_bookmarksWindow->slotAddBookmark(p->webviewUrl(),
					     p->webviewIcon(),
					     p->title(),
					     p->description(),
					     now,
					     now);
	}
    }
}

void dooble::highlightBookmarkButton(void)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->currentWidget());

  if(p)
    ui.locationLineEdit->setBookmarkColor(p->isBookmarked());
  else
    ui.locationLineEdit->setBookmarkColor(false);
}

void dooble::slotBookmarksChanged(void)
{
  highlightBookmarkButton();
}

qint64 dooble::id(void) const
{
  return m_id;
}

void dooble::reinstate(void)
{
  if(!dmisc::passphraseWasAuthenticated())
    return;
  else if(!s_settings.value("settingsWindow/sessionRestoration", true).
	  toBool())
    return;
  else if(s_instances > 1)
    return;

  QFileInfo fileInfo(dooble::s_homePath + QDir::separator() + ".crashed");

  if(!fileInfo.exists())
    return;

  dreinstatedooble *reinstateWidget = new dreinstatedooble(this);

  if(reinstateWidget->isEmpty())
    {
      reinstateWidget->deleteLater();
      QFile::remove(dooble::s_homePath + QDir::separator() + ".crashed");
      return;
    }

  connect(reinstateWidget,
	  SIGNAL(close(void)),
	  this,
	  SLOT(slotCloseTab(void)));

  QSettings settings
    (s_settings.value("iconSet").toString(), QSettings::IniFormat);

  settings.beginGroup("mainWindow");

  int index = ui.tabWidget->addTab
    (reinstateWidget,
     QIcon(settings.value("actionRestore").toString()),
     tr("Restore Session"));

  ui.tabWidget->setTabToolTip(index, tr("Restore Session"));
  ui.tabWidget->setTabsClosable(ui.tabWidget->count() > 1);
  ui.tabWidget->setCurrentWidget(reinstateWidget);

  QAction *action = new QAction(tr("Restore Session"), this);

  action->setData("Restore Session");
  action->setIcon
    (QIcon(settings.value("actionRestore").toString()));
  connect(action, SIGNAL(triggered(void)), this,
	  SLOT(slotShowRestoreSessionTab(void)));
  reinstateWidget->setTabAction(action);
  prepareTabsMenu();
}

void dooble::slotShowRestoreSessionTab(void)
{
  dreinstatedooble *reinstateWidget = qobject_cast<dreinstatedooble *>
    (findChild<dreinstatedooble *> ());

  if(reinstateWidget)
    ui.tabWidget->setCurrentWidget(reinstateWidget);
}

void dooble::slotShowAddOns(void)
{
  QAction *action = qobject_cast<QAction *> (sender());

  if(!action)
    return;

  for(int i = 0; i < ui.tabWidget->count(); i++)
    {
      dplugintab *plugTab = qobject_cast<dplugintab *>
	(ui.tabWidget->widget(i));

      if(plugTab && action == plugTab->tabAction())
	{
	  ui.tabWidget->setCurrentIndex(i);
	  break;
	}
    }
}

void dooble::remindUserToSetPassphrase(void)
{
  QSettings settings(dooble::s_settings.value("iconSet").toString(),
		     QSettings::IniFormat);
  QMessageBox mb(this);

#ifdef Q_WS_MAC
  mb.setAttribute(Qt::WA_MacMetalStyle, false);
#endif
  mb.setStyleSheet
    ("QMessageBox {"
     "background:"
     "qlineargradient(spread:reflect, x1:0.994, "
     "y1:1, x2:1, y2:0, stop:0.130682 rgba(255, 0, 0, 255), "
     "stop:0.897727 rgba(255, 255, 0, 255))"
     "}"
     "QLabel {"
     "font-weight: bold;"
     "font-size: 15px;"
     "}");
  mb.setIcon(QMessageBox::Information);
  mb.setStandardButtons(QMessageBox::Ok);
  mb.setWindowTitle(tr("Dooble Web Browser: Reminder"));
  mb.setText(tr("A passphrase has not been prepared. "
		"Please visit the Safe panel in the Settings window and "
		"choose a passphrase. "
		"Once a passphrase is selected, bookmarks, cookies, "
		"and all other essential information will be available "
		"in future sessions."));
#ifdef Q_WS_WIN
  foreach(QPushButton *pushButton,
	  mb.findChildren<QPushButton *> ())
    pushButton->setStyleSheet
    ("QPushButton { "
     "color: black; "
     "background-color: "
     "QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #eef, "
     "stop: 1 #ccf); "
     "border-width: 1px; "
     "border-color: #339; "
     "border-style: solid; "
     "border-radius: 8px; "
     "padding: 3px; "
     "font-size: 12px; "
     "padding-left: 5px; "
     "padding-right: 5px; "
     "min-width: 50px; "
     "max-width: 70px; "
     "min-height: 16px; "
     "max-height: 16px; "
     "}");
#endif

  for(int i = 0; i < mb.buttons().size(); i++)
    if(mb.buttonRole(mb.buttons().at(i)) == QMessageBox::AcceptRole)
      mb.buttons().at(i)->setIcon
	(QIcon(settings.value("okButtonIcon").toString()));

  mb.setWindowIcon
    (QIcon(settings.value("mainWindow/windowIcon").toString()));
  mb.exec();
}

void dooble::slotReloadTab(const int index)
{
  dview *p = qobject_cast<dview *> (ui.tabWidget->widget(index));

  if(p)
    {
      if(p->isModified() && !warnAboutLeavingModifiedTab())
	return;

      p->reload();
    }
}

void dooble::s_makeCrashFile(void)
{
  if(s_crashFileName == 0 &&
     dmisc::passphraseWasAuthenticated() &&
     s_settings.value("settingsWindow/sessionRestoration", true).toBool())
    {
      QString str(dooble::s_homePath);

      str.append(QDir::separator());
      str.append(".crashed");
      s_crashFileName = new char[str.length() + 1];
      memset(s_crashFileName, 0, str.length() + 1);
      strncpy(s_crashFileName, str.toStdString().c_str(), str.length());
    }
}

void dooble::s_removeCrashFile(void)
{
  if(s_crashFileName &&
     dmisc::passphraseWasAuthenticated())
    {
      delete []s_crashFileName;
      s_crashFileName = 0;
    }
}

void dooble::slotErrorLogged(void)
{
  sb.errorLogToolButton->setVisible(true);
}

void dooble::slotAboutToShowBackForwardMenu(void)
{
  prepareNavigationButtonMenus
    (qobject_cast<dview *> (ui.tabWidget->currentWidget()),
     qobject_cast<QMenu *> (sender()));
}

void dooble::slotAboutToShowBookmarksMenu(void)
{
  initializeBookmarksMenu();
}

void dooble::slotOffline(bool state)
{
  QSettings settings;

  settings.setValue("mainWindow/offlineMode", state);
  s_settings["mainWindow/offlineMode"] = state;
}

void dooble::slotPercentReadFromCache(const int percent)
{
  if(percent >= 100)
    sb.statusLabel->clear();
  else
    sb.statusLabel->setText(tr("Populating the cache... %1% completed.").
			    arg(percent));
}

void dooble::slotPercentWrittenToDisk(const int percent)
{
  if(percent >= 100)
    sb.statusLabel->clear();
  else
    sb.statusLabel->setText(tr("Committing the cache to disk... "
			       "%1% completed.").arg(percent));
}
