/* * This file is part of smolbote. It's copyrighted by the contributors recorded * in the version control history of the file, available from its original * location: https://neueland.iserlohn-fortress.net/smolbote.hg * * SPDX-License-Identifier: GPL-3.0 */ #include "mainwindow.h" #include "addressbar/addressbar.h" #include "browser.h" #include "subwindow.h" #include "webengine/webview.h" #include "widgets/dockwidget.h" #include "widgets/navigationbar.h" #include "widgets/searchform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "session.h" #include #ifdef PLASMA_BLUR #include #endif MainWindow::MainWindow(std::shared_ptr &config, QWidget *parent) : QMainWindow(parent) , mdiArea(new QMdiArea(this)) { Q_ASSERT(config); m_config = config; #ifdef PLASMA_BLUR setAttribute(Qt::WA_TranslucentBackground, true); KWindowEffects::enableBlurBehind(this->winId(), true); #endif // create UI setWindowTitle(QString::fromStdString(config->value("mainwindow.title").value())); resize(config->value("mainwindow.width").value(), config->value("mainwindow.height").value()); if(config->value("mainwindow.maximized").value()) { setWindowState(Qt::WindowMaximized); } show(); createMenuBar(); navigationToolBar = new NavigationBar(config->section("navigation"), this); navigationToolBar->setMovable(config->value("navigation.movable").value()); addToolBar(Qt::TopToolBarArea, navigationToolBar); navigationToolBar->connectWebView(nullptr); addressBar = new AddressBar(config->section("addressbar"), this); navigationToolBar->addWidget(addressBar); mdiArea->setBackground(Qt::NoBrush); setCentralWidget(mdiArea); mdiArea->setFocus(); // status bar searchBox = new SearchForm(this); statusBar()->addPermanentWidget(searchBox); searchBox->setVisible(false); // connect signlas connect(mdiArea, &QMdiArea::subWindowActivated, this, [this](QMdiSubWindow *window) { disconnect(viewChangedConnection); disconnect(searchBoxConnection); disconnect(statusBarConnection); subWindowAction->setMenu(nullptr); auto *w = qobject_cast(window); if(w == nullptr) { // no current subwindow, clear everything setView(nullptr); subWindowAction->setMenu(nullptr); } else { setView(w->currentView()); subWindowAction->setMenu(w->systemMenu()); viewChangedConnection = connect(w, &SubWindow::currentViewChanged, this, &MainWindow::setView); statusBarConnection = connect(w, &SubWindow::showStatusMessage, statusBar(), &QStatusBar::showMessage); } }); // address bar connect(addressBar, &AddressBar::search, this, [this](const QString &term) { if(this->currentView) { currentView->search(term); currentView->setFocus(); } }); connect(addressBar, &AddressBar::load, this, [this](const QUrl &url) { if(this->currentView) { currentView->load(url); currentView->setFocus(); } }); connect(addressBar, &AddressBar::giveFocus, this, [this]() { if(this->currentView) { currentView->setFocus(); } }); // search box auto *searchAction = new QAction(this); m_config->setShortcut(searchAction, "mainwindow.shortcuts.search"); connect(searchAction, &QAction::triggered, this, [=]() { /* QTBUG-18665 * When focusing out of the search box and hiding it, the first * (or earlier?) subwindow gets activated for some reason. */ if(searchBox->isVisible()) { auto *w = mdiArea->currentSubWindow(); searchBox->hide(); mdiArea->setActiveSubWindow(w); } else { searchBox->show(); } }); QMainWindow::addAction(searchAction); } MainWindow::~MainWindow() { disconnect(viewChangedConnection); disconnect(searchBoxConnection); disconnect(statusBarConnection); disconnect(addressBar); } void MainWindow::createMenuBar() { Q_CHECK_PTR(mdiArea); // smolbote menu auto *smolboteMenu = menuBar()->addMenu(qApp->applicationDisplayName()); auto *subwindowAction = smolboteMenu->addAction(tr("New subwindow"), this, [this]() { createSubWindow(); }); m_config->setShortcut(subwindowAction, "mainwindow.shortcuts.newGroup"); auto *windowAction = smolboteMenu->addAction(tr("New window"), this, []() { auto *browser = qobject_cast(qApp); if(browser) browser->createWindow(); }); m_config->setShortcut(windowAction, "mainwindow.shortcuts.newWindow"); smolboteMenu->addSeparator(); auto *aboutAction = smolboteMenu->addAction(tr("About"), qobject_cast(qApp), &Browser::about); m_config->setShortcut(aboutAction, "mainwindow.shortcuts.about"); smolboteMenu->addAction(tr("About Qt"), qApp, &QApplication::aboutQt); smolboteMenu->addSeparator(); auto *quitAction = smolboteMenu->addAction(tr("Quit"), qApp, &QApplication::quit); m_config->setShortcut(quitAction, "mainwindow.shortcuts.quit"); // session menu auto *sessionMenu = menuBar()->addMenu(tr("Session")); sessionMenu->addAction(tr("Save Session"), this, [this]() { const QString filename = QFileDialog::getSaveFileName(this, tr("Save Session"), QDir::homePath(), tr("JSON (*.json)")); QFile output(filename); if(output.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { output.write(QJsonDocument(Session::toJsonObject(this)).toJson()); output.close(); } }); sessionMenu->addAction(tr("Load Session"), this, [this]() { const QString filename = QFileDialog::getOpenFileName(this, tr("Load Session"), QDir::homePath(), tr("JSON (*.json)")); QFile json(filename); if(json.open(QIODevice::ReadOnly | QIODevice::Text)) { auto *browser = qobject_cast(qApp); browser->sendMessage(json.readAll()); json.close(); } }); // window menu auto *windowMenu = menuBar()->addMenu(tr("Window")); auto *tileAction = windowMenu->addAction(tr("Tile windows"), mdiArea, &QMdiArea::tileSubWindows); m_config->setShortcut(tileAction, "mainwindow.shortcuts.tileWindows"); auto *cascadeAction = windowMenu->addAction(tr("Cascade windows"), mdiArea, &QMdiArea::cascadeSubWindows); m_config->setShortcut(cascadeAction, "mainwindow.shortcuts.cascadeWindows"); subWindowAction = windowMenu->addAction(tr("Current window")); // tools menu toolsMenu = menuBar()->addMenu(tr("Tools")); // debug menu #ifdef QT_DEBUG auto *debugMenu = menuBar()->addMenu(tr("Debug")); debugMenu->addAction(tr("Print window session"), [this]() { auto json = Session::toJsonObject(this); qDebug("session data >>>\n%s\n<<<", qUtf8Printable(QJsonDocument(json).toJson())); }); #endif } void MainWindow::addAction(ActionLocation where, QAction *action) { switch(where) { case ToolsMenu: toolsMenu->addAction(action); break; default: QMainWindow::addAction(action); break; } } void MainWindow::addDockWidget(Qt::DockWidgetArea area, QWidget *widget) { QDockWidget *lastDock = nullptr; for(QDockWidget *dock : findChildren()) { if(dockWidgetArea(dock) == area) lastDock = dock; } DockWidget *dock = new DockWidget(widget->windowTitle(), this); dock->setMinimumWidth(460); dock->setWidget(widget); if(lastDock == nullptr) QMainWindow::addDockWidget(area, dock); else tabifyDockWidget(lastDock, dock); } void MainWindow::removeDockWidget(QWidget *widget) { const auto docks = this->findChildren(); for(QDockWidget *dock : docks) { if(dock->widget() == widget) { dock->widget()->setParent(nullptr); dock->close(); } } } void MainWindow::createTab(const QUrl &url) { auto *w = qobject_cast(mdiArea->currentSubWindow()); if(w == nullptr) { w = createSubWindow(url.toString()); } else { w->addTab(url); } } const QVector MainWindow::subWindows() const { QVector list; for(auto *w : mdiArea->subWindowList()) { auto *subwindow = qobject_cast(w); if(subwindow != nullptr) list.append(subwindow); } return list; } SubWindow *MainWindow::currentSubWindow() const { return qobject_cast(mdiArea->currentSubWindow()); } SubWindow *MainWindow::createSubWindow(WebProfile *profile) { auto *w = new SubWindow(m_config->section("window"), this); w->setProfile(profile); mdiArea->addSubWindow(w); w->showMaximized(); w->setFocus(); return w; } SubWindow *MainWindow::createSubWindow(const QString &url) { auto *w = new SubWindow(m_config->section("window"), this); mdiArea->addSubWindow(w); w->showMaximized(); w->setFocus(); w->addTab(url); return w; } void MainWindow::setView(WebView *view) { if(currentView) { // disconnect old view disconnect(currentView, 0, addressBar, 0); } currentView = view; if(view) { addressBar->setPageMenu(view->pageMenu()); addressBar->setToolsMenu(view->toolsMenu()); connect(view, &WebView::urlChanged, addressBar, &AddressBar::setUrl); addressBar->setUrl(view->url()); connect(view, &WebView::loadProgress, addressBar, &AddressBar::setProgress); addressBar->setProgress(100); } else { addressBar->setPageMenu(nullptr); addressBar->setToolsMenu(nullptr); addressBar->setUrl(QUrl()); addressBar->setProgress(100); } navigationToolBar->connectWebView(view); searchBox->setView(view); } void MainWindow::closeEvent(QCloseEvent *event) { if(mdiArea->subWindowList().count() > 1) { int choice = QMessageBox::question(this, tr("Close multiple subwindows?"), tr("Do you want to close all subwindows?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if(choice == QMessageBox::No) { event->ignore(); return; } } mdiArea->closeAllSubWindows(); if(mdiArea->currentSubWindow()) event->ignore(); else event->accept(); }