File indexing completed on 2024-05-12 05:20:43

0001 /*
0002  * kmail: KDE mail client
0003  * SPDX-FileCopyrightText: 1996-1998 Stefan Taferner <taferner@kde.org>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  *
0007  */
0008 
0009 #include "kmmainwin.h"
0010 #include "kmmainwidget.h"
0011 #include "tag/tagactionmanager.h"
0012 #include <Libkdepim/ProgressStatusBarWidget>
0013 #include <Libkdepim/StatusbarProgressWidget>
0014 #include <PimCommon/BroadcastStatus>
0015 #include <PimCommon/NeedUpdateVersionUtils>
0016 #include <PimCommon/NeedUpdateVersionWidget>
0017 
0018 #include <KConfigGroup>
0019 #include <KToolBar>
0020 #include <QApplication>
0021 #include <QStatusBar>
0022 #include <QTimer>
0023 
0024 #include <KAboutData>
0025 #include <KConfig>
0026 #include <KConfigGui>
0027 #include <KEditToolBar>
0028 #include <KLocalizedString>
0029 #include <KMessageBox>
0030 #include <KStandardAction>
0031 #include <KXMLGUIFactory>
0032 #include <QMenuBar>
0033 #include <QWindow>
0034 
0035 #include <QLabel>
0036 #include <QVBoxLayout>
0037 
0038 // signal handler for SIGINT & SIGTERM
0039 #ifdef Q_OS_UNIX
0040 #include <KSignalHandler>
0041 #include <signal.h>
0042 #include <unistd.h>
0043 #endif
0044 
0045 #include <chrono>
0046 
0047 using namespace std::chrono_literals;
0048 
0049 KMMainWin::KMMainWin(QWidget *)
0050     : KXmlGuiWindow(nullptr)
0051     , mMessageLabel(new QLabel(i18n("Starting...")))
0052 
0053 {
0054 #ifdef Q_OS_UNIX
0055     /**
0056      * Set up signal handler for SIGINT and SIGTERM
0057      */
0058     KSignalHandler::self()->watchSignal(SIGINT);
0059     KSignalHandler::self()->watchSignal(SIGTERM);
0060     connect(KSignalHandler::self(), &KSignalHandler::signalReceived, this, [this](int signal) {
0061         if (signal == SIGINT || signal == SIGTERM) {
0062             // Intercept console.
0063             printf("Shutting down...\n");
0064             slotQuit();
0065         }
0066     });
0067 #endif
0068     setObjectName(QLatin1StringView("kmail-mainwindow#"));
0069 
0070     resize(700, 500); // The default size
0071 
0072     auto mainWidget = new QWidget(this);
0073     auto mainWidgetLayout = new QVBoxLayout(mainWidget);
0074     mainWidgetLayout->setContentsMargins({});
0075     if (PimCommon::NeedUpdateVersionUtils::checkVersion()) {
0076         const auto status = PimCommon::NeedUpdateVersionUtils::obsoleteVersionStatus(KAboutData::applicationData().version(), QDate::currentDate());
0077         if (status != PimCommon::NeedUpdateVersionUtils::ObsoleteVersion::NotObsoleteYet) {
0078             auto needUpdateVersionWidget = new PimCommon::NeedUpdateVersionWidget(this);
0079             mainWidgetLayout->addWidget(needUpdateVersionWidget);
0080             qDebug() << " KAboutData::applicationData().version() " << KAboutData::applicationData().version();
0081             needUpdateVersionWidget->setObsoleteVersion(status);
0082         }
0083     }
0084 
0085     mKMMainWidget = new KMMainWidget(this, this, actionCollection());
0086     mainWidgetLayout->addWidget(mKMMainWidget);
0087 
0088     // Don't initialize in constructor. We need this statusbar created
0089     // Bug 460289
0090     mProgressBar = new KPIM::ProgressStatusBarWidget(statusBar(), this);
0091     connect(mKMMainWidget, &KMMainWidget::recreateGui, this, &KMMainWin::slotUpdateGui);
0092     setCentralWidget(mainWidget);
0093     setupStatusBar();
0094     if (!kmkernel->xmlGuiInstanceName().isEmpty()) {
0095         setComponentName(kmkernel->xmlGuiInstanceName(), i18n("KMail2"));
0096     }
0097     setStandardToolBarMenuEnabled(true);
0098 
0099     KStandardAction::configureToolbars(this, &KMMainWin::slotEditToolbars, actionCollection());
0100 
0101     KStandardAction::keyBindings(this, &KMMainWin::slotConfigureShortcuts, actionCollection());
0102 
0103     mShowMenuBarAction = KStandardAction::showMenubar(this, &KMMainWin::slotToggleMenubar, actionCollection());
0104     if (menuBar()) {
0105         mHamburgerMenu = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection());
0106         mHamburgerMenu->setShowMenuBarAction(mShowMenuBarAction);
0107         mHamburgerMenu->setMenuBar(menuBar());
0108         connect(mHamburgerMenu, &KHamburgerMenu::aboutToShowMenu, this, [this]() {
0109             updateHamburgerMenu();
0110             // Immediately disconnect. We only need to run this once, but on demand.
0111             // NOTE: The nullptr at the end disconnects all connections between
0112             // q and mHamburgerMenu's aboutToShowMenu signal.
0113             disconnect(mHamburgerMenu, &KHamburgerMenu::aboutToShowMenu, this, nullptr);
0114         });
0115     }
0116 
0117     KStandardAction::quit(this, &KMMainWin::slotQuit, actionCollection());
0118     createGUI(QStringLiteral("kmmainwin.rc"));
0119 
0120     // must be after createGUI, otherwise e.g toolbar settings are not loaded
0121     setAutoSaveSettings(KMKernel::self()->config()->group(QStringLiteral("Main Window")));
0122 
0123     connect(PimCommon::BroadcastStatus::instance(), &PimCommon::BroadcastStatus::statusMsg, this, &KMMainWin::displayStatusMessage);
0124 
0125     connect(mKMMainWidget, &KMMainWidget::captionChangeRequest, this, qOverload<const QString &>(&KMainWindow::setCaption));
0126 
0127     mKMMainWidget->updateQuickSearchLineText();
0128     mShowMenuBarAction->setChecked(KMailSettings::self()->showMenuBar());
0129     slotToggleMenubar(true);
0130     connect(guiFactory(), &KXMLGUIFactory::shortcutsSaved, this, &KMMainWin::slotShortcutSaved);
0131 
0132     mShowFullScreenAction = KStandardAction::fullScreen(nullptr, nullptr, this, actionCollection());
0133     actionCollection()->setDefaultShortcut(mShowFullScreenAction, Qt::Key_F11);
0134     connect(mShowFullScreenAction, &QAction::toggled, this, &KMMainWin::slotFullScreen);
0135     KMKernel::self()->setSystemTryAssociatedWindow(windowHandle());
0136 }
0137 
0138 KMMainWin::~KMMainWin()
0139 {
0140     // Avoids a crash if there are any Akonadi jobs running, which may
0141     // attempt to display a status message when they are killed.
0142     disconnect(PimCommon::BroadcastStatus::instance(), &PimCommon::BroadcastStatus::statusMsg, this, nullptr);
0143 }
0144 
0145 void KMMainWin::slotFullScreen(bool t)
0146 {
0147     KToggleFullScreenAction::setFullScreen(this, t);
0148     QMenuBar *mb = menuBar();
0149     if (t) {
0150         auto b = new QToolButton(mb);
0151         b->setDefaultAction(mShowFullScreenAction);
0152         b->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Ignored));
0153         b->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
0154         mb->setCornerWidget(b, Qt::TopRightCorner);
0155         b->setVisible(true);
0156         b->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0157     } else {
0158         QWidget *w = mb->cornerWidget(Qt::TopRightCorner);
0159         if (w) {
0160             w->deleteLater();
0161         }
0162     }
0163 }
0164 
0165 void KMMainWin::updateHamburgerMenu()
0166 {
0167     auto menu = new QMenu(this);
0168     menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Open)));
0169     menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::SaveAs)));
0170     menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Print)));
0171     menu->addSeparator();
0172     menu->addAction(actionCollection()->action(QStringLiteral("check_mail")));
0173     menu->addAction(actionCollection()->action(QStringLiteral("check_mail_in")));
0174     menu->addAction(actionCollection()->action(QStringLiteral("send_queued")));
0175     menu->addAction(actionCollection()->action(QStringLiteral("send_queued_via")));
0176     menu->addSeparator();
0177 
0178     menu->addAction(actionCollection()->action(QStringLiteral("kmail_configure_kmail")));
0179     menu->addAction(actionCollection()->action(QStringLiteral("kmail_configure_notifications")));
0180     menu->addSeparator();
0181 
0182     menu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Quit)));
0183     mHamburgerMenu->setMenu(menu);
0184 }
0185 
0186 KMMainWidget *KMMainWin::mainKMWidget() const
0187 {
0188     return mKMMainWidget;
0189 }
0190 
0191 void KMMainWin::displayStatusMessage(const QString &aText)
0192 {
0193     if (!statusBar() || !mProgressBar->littleProgress()) {
0194         return;
0195     }
0196     const int statusWidth = statusBar()->width() - mProgressBar->littleProgress()->width() - fontMetrics().maxWidth();
0197 
0198     const QString text = fontMetrics().elidedText(QLatin1Char(' ') + aText, Qt::ElideRight, statusWidth);
0199 
0200     // ### FIXME: We should disable richtext/HTML (to avoid possible denial of service attacks),
0201     // but this code would double the size of the status bar if the user hovers
0202     // over an <foo@bar.com>-style email address :-(
0203     //  text.replace("&", "&amp;");
0204     //  text.replace("<", "&lt;");
0205     //  text.replace(">", "&gt;");
0206     mMessageLabel->setText(text);
0207     mMessageLabel->setToolTip(aText);
0208 }
0209 
0210 void KMMainWin::slotToggleMenubar(bool dontShowWarning)
0211 {
0212     if (menuBar()) {
0213         if (mShowMenuBarAction->isChecked()) {
0214             menuBar()->show();
0215         } else {
0216             if (!dontShowWarning && (!toolBar()->isVisible() || !toolBar()->actions().contains(mHamburgerMenu))) {
0217                 const QString accel = mShowMenuBarAction->shortcut().toString();
0218                 KMessageBox::information(this,
0219                                          i18n("<qt>This will hide the menu bar completely."
0220                                               " You can show it again by typing %1.</qt>",
0221                                               accel),
0222                                          i18nc("@title:window", "Hide menu bar"),
0223                                          QStringLiteral("HideMenuBarWarning"));
0224             }
0225             menuBar()->hide();
0226         }
0227         KMailSettings::self()->setShowMenuBar(mShowMenuBarAction->isChecked());
0228     }
0229 }
0230 
0231 void KMMainWin::slotEditToolbars()
0232 {
0233     KConfigGroup grp = KMKernel::self()->config()->group(QStringLiteral("Main Window"));
0234     saveMainWindowSettings(grp);
0235     QPointer<KEditToolBar> dlg = new KEditToolBar(guiFactory(), this);
0236     connect(dlg.data(), &KEditToolBar::newToolBarConfig, this, &KMMainWin::slotUpdateGui);
0237 
0238     dlg->exec();
0239     delete dlg;
0240 }
0241 
0242 void KMMainWin::slotUpdateGui()
0243 {
0244     // remove dynamically created actions before editing
0245     mKMMainWidget->clearFilterActions();
0246     mKMMainWidget->tagActionManager()->clearActions();
0247     mKMMainWidget->clearPluginActions();
0248 
0249     createGUI(QStringLiteral("kmmainwin.rc"));
0250     applyMainWindowSettings(KMKernel::self()->config()->group(QStringLiteral("Main Window")));
0251 
0252     // plug dynamically created actions again
0253     mKMMainWidget->initializeFilterActions(false);
0254     mKMMainWidget->tagActionManager()->createActions();
0255     // FIXME mKMMainWidget->initializePluginActions();
0256 }
0257 
0258 void KMMainWin::setupStatusBar()
0259 {
0260     /* Create a progress dialog and hide it. */
0261     mMessageLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
0262     statusBar()->addWidget(mMessageLabel);
0263 
0264     QTimer::singleShot(2s, PimCommon::BroadcastStatus::instance(), &PimCommon::BroadcastStatus::reset);
0265     statusBar()->addPermanentWidget(mKMMainWidget->dkimWidgetInfo());
0266     statusBar()->addPermanentWidget(mKMMainWidget->zoomLabelIndicator());
0267     statusBar()->addPermanentWidget(mKMMainWidget->vacationScriptIndicator());
0268     statusBar()->addPermanentWidget(mProgressBar->littleProgress());
0269 }
0270 
0271 void KMMainWin::slotQuit()
0272 {
0273     mReallyClose = true;
0274     close();
0275 }
0276 
0277 //-----------------------------------------------------------------------------
0278 bool KMMainWin::restoreDockedState(int n)
0279 {
0280     // Default restore behavior is to show the window once it is restored.
0281     // Override this if the main window was hidden in the system tray
0282     // when the session was saved.
0283     KConfigGroup config(KConfigGui::sessionConfig(), QString::number(n));
0284     const bool show = !config.readEntry("docked", false);
0285 
0286     return KMainWindow::restore(n, show);
0287 }
0288 
0289 void KMMainWin::showAndActivateWindow()
0290 {
0291     show();
0292     raise();
0293     activateWindow();
0294 }
0295 
0296 void KMMainWin::saveProperties(KConfigGroup &config)
0297 {
0298     // This is called by the session manager on log-off
0299     // Save the shown/hidden status so we can restore to the same state.
0300     KMainWindow::saveProperties(config);
0301     config.writeEntry("docked", isHidden());
0302 }
0303 
0304 bool KMMainWin::queryClose()
0305 {
0306     if (kmkernel->shuttingDown() || qApp->isSavingSession() || mReallyClose) {
0307         return true;
0308     }
0309     return kmkernel->canQueryClose();
0310 }
0311 
0312 void KMMainWin::slotConfigureShortcuts()
0313 {
0314     guiFactory()->showConfigureShortcutsDialog();
0315 }
0316 
0317 void KMMainWin::slotShortcutSaved()
0318 {
0319     mKMMainWidget->updateQuickSearchLineText();
0320 }
0321 
0322 #include "moc_kmmainwin.cpp"