/* * 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/gitea/aqua/smolbote * * SPDX-License-Identifier: GPL-3.0 */ #include "menubar.h" #include "bookmarks/bookmarkswidget.h" #include "browser.h" #include "configuration.h" #include "downloadswidget.h" #include "mainwindow.h" #include "session/savesessiondialog.h" #include "session/sessiondialog.h" #include "subwindow/subwindow.h" #include "webengine/webprofilemanager.h" #include "webengine/webview.h" #include "widgets/menusearch.h" #include #include #include #include #include #include #include #include #include #include #include #include inline void run_if(SubWindow *_subwindow, const std::function &f) { if(_subwindow != nullptr) f(_subwindow, _subwindow->currentTabIndex()); } inline void trigger_if(WebView *_view, QWebEnginePage::WebAction action) { if(_view != nullptr) _view->triggerPageAction(action); } inline QDialog *createDevToolsDialog(QWebEnginePage *page) { Q_CHECK_PTR(page); auto *popup = new QDialog(nullptr); popup->setWindowTitle(QObject::tr("Developer Tools")); popup->setAttribute(Qt::WA_DeleteOnClose, true); popup->resize(800, 600); auto *view = new QWebEngineView(popup); auto *devPage = new QWebEnginePage(view); view->setPage(devPage); auto *l = new QVBoxLayout(popup); l->setContentsMargins(0, 0, 0, 0); l->addWidget(view); page->setDevToolsPage(devPage); QObject::connect(popup, &QDialog::destroyed, [page]() { page->setDevToolsPage(nullptr); }); return popup; } MenuBar::MenuBar(MainWindow *parent) : QMenuBar(parent) { auto *browser = qobject_cast(qApp); Q_CHECK_PTR(browser); smolbote = this->addMenu(qApp->applicationName()); { auto *findMenu = smolbote->addMenu(tr("Find in menus")); auto *findWidget = new QWidgetAction(this); auto *find_lineEdit = new MenuSearch(this); findWidget->setDefaultWidget(find_lineEdit); findMenu->addAction(findWidget); connect(findMenu, &QMenu::aboutToShow, [findMenu, find_lineEdit]() { find_lineEdit->clear(); const auto actions = findMenu->actions(); for(int i = 1; i < actions.length(); i++) findMenu->removeAction(actions.at(i)); find_lineEdit->setFocus(); }); connect(find_lineEdit, &QLineEdit::textEdited, [this, findMenu](const QString &text) { // clear menu const auto actions = findMenu->actions(); for(int i = 1; i < actions.length(); i++) findMenu->removeAction(actions.at(i)); if(text.isEmpty()) return; // findChildren for(QAction *a : findChildren()) { if(a->text().contains(text)) findMenu->addAction(a); } }); smolbote->addSeparator(); Configuration conf; const QString sessionPath = conf.value("session.path").value(); auto *actionSaveSession = smolbote->addAction(tr("Save Session"), parent, [sessionPath]() { auto *sessionDialog = new SaveSessionDialog(nullptr); if(sessionDialog->exec() == QDialog::Accepted) sessionDialog->save(sessionPath); }); setShortcut(actionSaveSession, "shortcuts.session.save"); auto *actionOpenSession = smolbote->addAction(tr("Open Session"), parent, [parent]() { auto *sessionDialog = new SessionDialog(parent); sessionDialog->exec(); }); setShortcut(actionOpenSession, "shortcuts.session.open"); smolbote->addSeparator(); auto *actionBookmarks = smolbote->addAction(tr("Bookmarks"), browser, [browser, parent]() { browser->showWidget(browser->bookmarks(), parent); }); setShortcut(actionBookmarks, "shortcuts.window.bookmarks.show"); auto *actionDownloads = smolbote->addAction(tr("Downloads"), browser, [browser, parent]() { browser->showWidget(browser->downloads(), parent); }); setShortcut(actionDownloads, "shortcuts.window.downloads.show"); smolbote->addSeparator(); smolbote->addAction(tr("Load Plugin"), browser, [browser]() { const QString path = QFileDialog::getOpenFileName(nullptr, tr("Select Plugin"), QDir::homePath(), tr("Plugins (*.so)")); browser->loadPlugins(QStringList(path)); }); pluginInsertLocation = smolbote->addSeparator(); auto *actionAbout = smolbote->addAction(tr("About"), browser, &Browser::about); setShortcut(actionAbout, "shortcuts.window.about"); smolbote->addSeparator(); auto *actionQuit = smolbote->addAction(tr("Quit"), qApp, &QApplication::quit); setShortcut(actionQuit, "shortcuts.window.quit"); } window = this->addMenu(tr("&Window")); { auto *actionNewWindow = window->addAction(tr("New Window"), browser, &Browser::createWindow); setShortcut(actionNewWindow, "shortcuts.window.newwindow"); auto *actionNewSubwindow = window->addAction(tr("New Subwindow"), parent, [parent]() { parent->createSubWindow(nullptr, true); }); setShortcut(actionNewSubwindow, "shortcuts.window.newgroup"); auto *actionTileSubwindows = window->addAction(tr("Tile Subwindows"), parent->mdiArea, &QMdiArea::tileSubWindows); setShortcut(actionTileSubwindows, "shortcuts.subwindow.tile"); auto *actionCascadeSubwindows = window->addAction(tr("Cascade Subwindows"), parent->mdiArea, &QMdiArea::cascadeSubWindows); setShortcut(actionCascadeSubwindows, "shortcuts.subwindow.cascade"); window->addSeparator()->setText(tr("Toolbars")); for(auto *toolbar : parent->findChildren()) { auto *action = window->addAction(toolbar->windowTitle()); action->setCheckable(true); action->setChecked(toolbar->isVisible()); connect(toolbar, &QToolBar::visibilityChanged, action, &QAction::setChecked); connect(action, &QAction::triggered, toolbar, &QToolBar::setVisible); setShortcut(action, qUtf8Printable("shortcuts."+toolbar->objectName()+".show")); } window->addSeparator()->setText(tr("Subwindows")); } auto *subwindow = this->addMenu(tr("&Subwindow")); { auto *actionNewTab = subwindow->addAction(tr("New Tab"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int) { const int index = _subwindow->addTab(); _subwindow->setCurrentTab(index); }); }); setShortcut(actionNewTab, "shortcuts.subwindow.newtab"); subwindow->addSeparator(); auto *actionRestoreTab = subwindow->addAction(tr("Restore last tab"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int) { const int index = _subwindow->restoreLastTab(); _subwindow->setCurrentTab(index); }); }); setShortcut(actionRestoreTab, "shortcuts.subwindow.restoretab"); auto *restoreTabsMenu = subwindow->addMenu(tr("Restore previous tab")); connect(restoreTabsMenu, &QMenu::aboutToShow, parent, [parent, restoreTabsMenu]() { restoreTabsMenu->clear(); auto *_subwindow = parent->currentSubWindow(); if(_subwindow != nullptr) { _subwindow->restoreTabMenu(restoreTabsMenu); } }); subwindow->addSeparator()->setText(tr("Tab Settings")); auto *actionPinTab = subwindow->addAction(tr("Pin tab")); actionPinTab->setCheckable(true); actionPinTab->setEnabled(false); auto *actionLockClose = subwindow->addAction(tr("Prevent tab from closing"), parent, [parent](bool checked) { run_if(parent->currentSubWindow(), [checked](SubWindow *_subwindow, int currentIdx) { auto data = _subwindow->tabData(currentIdx); data.closeLocked = checked; _subwindow->setTabData(data, currentIdx); }); }); actionLockClose->setCheckable(true); auto *actionLockRefresh = subwindow->addAction(tr("Prevent tab from refreshing"), parent, [parent](bool checked) { run_if(parent->currentSubWindow(), [checked](SubWindow *_subwindow, int currentIdx) { auto data = _subwindow->tabData(currentIdx); data.refreshLocked = checked; _subwindow->setTabData(data, currentIdx); }); }); actionLockRefresh->setCheckable(true); connect(subwindow, &QMenu::aboutToShow, subwindow, [=]() { run_if(parent->currentSubWindow(), [=](SubWindow *_subwindow, int currentIdx) { auto data = _subwindow->tabData(currentIdx); actionLockClose->setChecked(data.closeLocked); actionLockRefresh->setChecked(data.refreshLocked); }); }); subwindow->addSeparator()->setText(tr("Tab Actions")); auto *leftTab = subwindow->addAction(tr("Switch to tab on the left"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { _subwindow->setCurrentTab(qMax(0, currentIdx - 1)); }); }); setShortcut(leftTab, "shortcuts.subwindow.tableft"); auto *moveTabLeft = subwindow->addAction(tr("Move tab left"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { _subwindow->moveTab(currentIdx, currentIdx - 1); }); }); setShortcut(moveTabLeft, "shortcuts.subwindow.movetableft"); auto *rightTab = subwindow->addAction(tr("Switch to tab on the right"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { _subwindow->setCurrentTab(qMin(currentIdx + 1, _subwindow->tabCount() - 1)); }); }); setShortcut(rightTab, "shortcuts.subwindow.tabright"); auto *moveTabRight = subwindow->addAction(tr("Move tab right"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { _subwindow->moveTab(currentIdx, currentIdx + 1); }); }); setShortcut(moveTabRight, "shortcuts.subwindow.movetabright"); subwindow->addSeparator(); auto *closeTab = subwindow->addAction(tr("Close tab"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { _subwindow->closeTab(currentIdx); }); }); setShortcut(closeTab, "shortcuts.subwindow.closetab"); subwindow->addAction(tr("Close tabs to the left"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { for(int i = currentIdx - 1; i >= 0; i--) { const auto data = _subwindow->tabData(i); if(!data.closeLocked) _subwindow->closeTab(i); } }); }); subwindow->addAction(tr("Close tabs to the right"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { for(int i = _subwindow->tabCount() - 1; i > currentIdx; i--) { const auto data = _subwindow->tabData(i); if(!data.closeLocked) _subwindow->closeTab(i); } }); }); subwindow->addAction(tr("Close all other tabs"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { for(int i = _subwindow->tabCount() - 1; i >= 0; i--) { if(i != currentIdx) { const auto data = _subwindow->tabData(i); if(!data.closeLocked) _subwindow->closeTab(i); } } }); }); subwindow->addSeparator(); subwindow->addAction(tr("Refresh tabs to the left"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { for(int i = 0; i < currentIdx; i++) { const auto data = _subwindow->tabData(i); if(!data.refreshLocked) _subwindow->view(i)->triggerPageAction(QWebEnginePage::Reload); } }); }); subwindow->addAction(tr("Refresh tabs to the right"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { for(int i = currentIdx + 1; i < _subwindow->tabCount(); i++) { const auto data = _subwindow->tabData(i); if(!data.refreshLocked) _subwindow->view(i)->triggerPageAction(QWebEnginePage::Reload); } }); }); subwindow->addAction(tr("Refresh all other tabs"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { for(int i = 0; i < _subwindow->tabCount(); i++) { if(i != currentIdx) { const auto data = _subwindow->tabData(i); if(!data.refreshLocked) _subwindow->view(i)->triggerPageAction(QWebEnginePage::Reload); } } }); }); subwindow->addSeparator(); auto *subwindowProfile = subwindow->addMenu(tr("Subwindow Profile")); connect(subwindowProfile, &QMenu::aboutToShow, subwindowProfile, [=]() { subwindowProfile->clear(); auto *_subwindow = parent->currentSubWindow(); if(_subwindow != nullptr) { profileMenu( subwindowProfile, [_subwindow](WebProfile *profile) { _subwindow->setProfile(profile); }, _subwindow->profile(), true); } }); } auto *page = this->addMenu(tr("&Page")); { page->addAction(tr("Create Bookmark"), parent, [browser, parent]() { if(parent->currentView != nullptr) browser->bookmarks()->addBookmark(parent->currentView->title(), parent->currentView->url().toString()); }); page->addSeparator(); page->addAction(tr("Save"), parent, [parent]() { if(parent->currentView != nullptr) parent->currentView->triggerPageAction(QWebEnginePage::SavePage); }); page->addAction(tr("Print"), parent, [parent]() { if(parent->currentView != nullptr) { auto *printer = new QPrinter(QPrinterInfo::defaultPrinter()); QPrintDialog dlg(printer, parent); if(dlg.exec() == QDialog::Accepted) { parent->currentView->page()->print(printer, [printer](bool success) { Q_UNUSED(success); delete printer; }); } } }); page->addAction(tr("Print to PDF"), parent, [parent]() { if(parent->currentView != nullptr) { const QString path = QFileDialog::getSaveFileName(parent, tr("Print to PDF"), QDir::homePath(), tr("PDF files (*.pdf)")); parent->currentView->page()->printToPdf(path); } }); page->addSeparator(); auto pageProfile = page->addMenu(tr("Page Profile")); connect(pageProfile, &QMenu::aboutToShow, pageProfile, [=]() { pageProfile->clear(); if(parent->currentView != nullptr) { profileMenu( pageProfile, [parent](WebProfile *profile) { parent->currentView->setProfile(profile); }, parent->currentView->profile(), true); } }); page->addSeparator(); page->addAction(tr("Developer Tools"), parent, [parent]() { if(parent->currentView != nullptr) { auto *dlg = createDevToolsDialog(parent->currentView->page()); dlg->show(); } }); page->addAction(tr("View Source"), parent, [parent]() { if(parent->currentView != nullptr) { parent->currentView->triggerPageAction(QWebEnginePage::ViewSource); } }); #ifdef QT_DEBUG // doesn't seem to do anything? page->addAction(tr("Inspect Element"), parent, [parent]() { if(parent->currentView != nullptr) { parent->currentView->triggerPageAction(QWebEnginePage::InspectElement); } }); #endif } this->addSeparator(); auto *edit = this->addMenu(tr("Edit")); { edit->addAction(tr("Undo"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::Undo); }); edit->addAction(tr("Redo"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::Redo); }); edit->addSeparator(); edit->addAction(tr("Cut"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::Cut); }); edit->addAction(tr("Copy"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::Copy); }); edit->addAction(tr("Paste"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::Paste); }); edit->addAction(tr("Paste and match Style"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::PasteAndMatchStyle); }); edit->addSeparator(); edit->addAction(tr("Select all"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::SelectAll); }); edit->addAction(tr("Clear selection"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::Unselect); }); edit->addSeparator()->setText(tr("Editing")); edit->addAction(tr("Make editable"), parent, [parent]() { if(parent->currentView != nullptr) parent->currentView->page()->runJavaScript("document.documentElement.contentEditable = true"); }); edit->addAction(tr("Bold"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::ToggleBold); }); edit->addAction(tr("Italic"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::ToggleItalic); }); edit->addAction(tr("Underline"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::ToggleUnderline); }); edit->addAction(tr("Strikethrough"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::ToggleStrikethrough); }); edit->addAction(tr("Align left"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::AlignLeft); }); edit->addAction(tr("Align right"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::AlignRight); }); edit->addAction(tr("Align center"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::AlignCenter); }); edit->addAction(tr("Align justified"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::AlignJustified); }); edit->addAction(tr("Indent"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::Indent); }); edit->addAction(tr("Outdent"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::Outdent); }); edit->addAction(tr("Insert ordered list"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::InsertOrderedList); }); edit->addAction(tr("Insert unordered list"), parent, [parent]() { trigger_if(parent->currentView, QWebEnginePage::InsertUnorderedList); }); } } QAction *MenuBar::insertPlugin(QMenu *menu) { return smolbote->insertMenu(pluginInsertLocation, menu); } void MenuBar::insertSubWindow(SubWindow *subwindow) { auto *action = window->addAction(subwindow->windowTitle()); action->setCheckable(true); connect(subwindow, &SubWindow::windowStateChanged, action, [action](Qt::WindowStates, Qt::WindowStates state) { action->setChecked(state.testFlag(Qt::WindowMaximized) | state.testFlag(Qt::WindowActive)); }); connect(subwindow, &SubWindow::windowTitleChanged, action, &QAction::setText); connect(subwindow, &SubWindow::destroyed, action, &QAction::deleteLater); auto *optionsMenu = new QMenu; optionsMenu->addAction(tr("Switch to"), subwindow, [subwindow]() { if(!subwindow->windowState().testFlag(Qt::WindowMaximized)) subwindow->showMaximized(); else subwindow->showNormal(); }); optionsMenu->addAction(tr("Close"), subwindow, [subwindow]() { subwindow->close(); }); action->setMenu(optionsMenu); }