File indexing completed on 2024-05-05 04:19:19

0001 // vim: set tabstop=4 shiftwidth=4 expandtab:
0002 /*
0003 Gwenview: an image viewer
0004 Copyright 2007 Aurélien Gâteau <agateau@kde.org>
0005 
0006 This program is free software; you can redistribute it and/or
0007 modify it under the terms of the GNU General Public License
0008 as published by the Free Software Foundation; either version 2
0009 of the License, or (at your option) any later version.
0010 
0011 This program is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU General Public License for more details.
0015 
0016 You should have received a copy of the GNU General Public License
0017 along with this program; if not, write to the Free Software
0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0019 
0020 */
0021 // Self
0022 #include "savebar.h"
0023 
0024 // Qt
0025 #include <QHBoxLayout>
0026 #include <QIcon>
0027 #include <QLabel>
0028 #include <QToolButton>
0029 #include <QToolTip>
0030 #include <QUrl>
0031 
0032 // KF
0033 #include <KActionCollection>
0034 #include <KColorScheme>
0035 #include <KIconLoader>
0036 #include <KLocalizedString>
0037 
0038 // Local
0039 #include "gwenview_app_debug.h"
0040 #include "lib/document/documentfactory.h"
0041 #include "lib/gwenviewconfig.h"
0042 #include "lib/memoryutils.h"
0043 #include "lib/paintutils.h"
0044 
0045 namespace Gwenview
0046 {
0047 QToolButton *createToolButton()
0048 {
0049     auto button = new QToolButton;
0050     button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0051     button->hide();
0052     return button;
0053 }
0054 
0055 struct SaveBarPrivate {
0056     SaveBar *q = nullptr;
0057     KActionCollection *mActionCollection = nullptr;
0058     QWidget *mSaveBarWidget = nullptr;
0059     QWidget *mTopRowWidget = nullptr;
0060     QToolButton *mUndoButton = nullptr;
0061     QToolButton *mRedoButton = nullptr;
0062     QToolButton *mSaveCurrentUrlButton = nullptr;
0063     QToolButton *mSaveAsButton = nullptr;
0064     QToolButton *mSaveAllButton = nullptr;
0065     QToolButton *mSaveAllFullScreenButton = nullptr;
0066     QLabel *mMessageLabel = nullptr;
0067     QLabel *mActionsLabel = nullptr;
0068     QFrame *mTooManyChangesFrame = nullptr;
0069     QUrl mCurrentUrl;
0070 
0071     void createTooManyChangesFrame()
0072     {
0073         mTooManyChangesFrame = new QFrame;
0074 
0075         // Icon
0076         auto iconLabel = new QLabel;
0077         QPixmap pix = QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(KIconLoader::SizeSmall);
0078         iconLabel->setPixmap(pix);
0079 
0080         // Text label
0081         auto textLabel = new QLabel;
0082         textLabel->setText(i18n("You have modified many images. To avoid memory problems, you should save your changes."));
0083 
0084         mSaveAllFullScreenButton = createToolButton();
0085 
0086         // Layout
0087         auto layout = new QHBoxLayout(mTooManyChangesFrame);
0088         layout->setContentsMargins(0, 0, 0, 0);
0089         layout->addWidget(iconLabel);
0090         layout->addWidget(textLabel);
0091         layout->addWidget(mSaveAllFullScreenButton);
0092         mTooManyChangesFrame->hide();
0093 
0094         // CSS
0095         KColorScheme scheme(mSaveBarWidget->palette().currentColorGroup(), KColorScheme::Window);
0096         QColor warningBackgroundColor = scheme.background(KColorScheme::NegativeBackground).color();
0097         QColor warningBorderColor = PaintUtils::adjustedHsv(warningBackgroundColor, 0, 150, 0);
0098         QColor warningColor = scheme.foreground(KColorScheme::NegativeText).color();
0099 
0100         QString css =
0101             ".QFrame {"
0102             "   background-color: %1;"
0103             "   border: 1px solid %2;"
0104             "   border-radius: 4px;"
0105             "   padding: 3px;"
0106             "}"
0107             ".QFrame QLabel {"
0108             "   color: %3;"
0109             "}";
0110         css = css.arg(warningBackgroundColor.name(), warningBorderColor.name(), warningColor.name());
0111         mTooManyChangesFrame->setStyleSheet(css);
0112     }
0113 
0114     void applyNormalStyleSheet()
0115     {
0116         const QColor bgColor = QToolTip::palette().base().color();
0117         const QColor borderColor = PaintUtils::adjustedHsv(bgColor, 0, 150, 0);
0118         const QColor fgColor = QToolTip::palette().text().color();
0119 
0120         QString css =
0121             "#saveBarWidget {"
0122             "   background-color: %1;"
0123             "   border-top: 1px solid %2;"
0124             "   border-bottom: 1px solid %2;"
0125             "   color: %3;"
0126             "}";
0127 
0128         css = css.arg(bgColor.name(), borderColor.name(), fgColor.name());
0129         mSaveBarWidget->setStyleSheet(css);
0130     }
0131 
0132     void applyFullScreenStyleSheet()
0133     {
0134         const QString css =
0135             "#saveBarWidget {"
0136             "   background-color: #333;"
0137             "}";
0138         mSaveBarWidget->setStyleSheet(css);
0139     }
0140 
0141     void updateTooManyChangesFrame(const QList<QUrl> &list)
0142     {
0143         qreal maxPercentageOfMemoryUsage = GwenviewConfig::percentageOfMemoryUsageWarning();
0144         qulonglong maxMemoryUsage = MemoryUtils::getTotalMemory() * maxPercentageOfMemoryUsage;
0145         qulonglong memoryUsage = 0;
0146         for (const QUrl &url : list) {
0147             Document::Ptr doc = DocumentFactory::instance()->load(url);
0148             memoryUsage += doc->memoryUsage();
0149         }
0150 
0151         mTooManyChangesFrame->setVisible(memoryUsage > maxMemoryUsage);
0152     }
0153 
0154     void updateTopRowWidget(const QList<QUrl> &lst)
0155     {
0156         QStringList links;
0157         QString message;
0158 
0159         if (lst.contains(mCurrentUrl)) {
0160             message = i18n("Current image modified");
0161 
0162             mUndoButton->show();
0163             mRedoButton->show();
0164 
0165             if (lst.size() > 1) {
0166                 QString previous = i18n("Previous modified image");
0167                 QString next = i18n("Next modified image");
0168                 if (mCurrentUrl == lst[0]) {
0169                     links << previous;
0170                 } else {
0171                     links << QStringLiteral("<a href='previous'>%1</a>").arg(previous);
0172                 }
0173                 if (mCurrentUrl == lst[lst.size() - 1]) {
0174                     links << next;
0175                 } else {
0176                     links << QStringLiteral("<a href='next'>%1</a>").arg(next);
0177                 }
0178             }
0179         } else {
0180             mUndoButton->hide();
0181             mRedoButton->hide();
0182 
0183             message = i18np("One image modified", "%1 images modified", lst.size());
0184             if (lst.size() > 1) {
0185                 links << QStringLiteral("<a href='first'>%1</a>").arg(i18n("Go to first modified image"));
0186             } else {
0187                 links << QStringLiteral("<a href='first'>%1</a>").arg(i18n("Go to it"));
0188             }
0189         }
0190 
0191         mSaveCurrentUrlButton->setVisible(lst.contains(mCurrentUrl));
0192         mSaveAsButton->setVisible(lst.contains(mCurrentUrl));
0193         mSaveAllButton->setVisible(lst.size() >= 1);
0194 
0195         mMessageLabel->setText(message);
0196         mMessageLabel->setMaximumWidth(mMessageLabel->minimumSizeHint().width());
0197         mActionsLabel->setText(links.join(QStringLiteral(" | ")));
0198     }
0199 
0200     void updateWidgetSizes()
0201     {
0202         auto layout = static_cast<QVBoxLayout *>(mSaveBarWidget->layout());
0203         int topRowHeight = q->window()->isFullScreen() ? 0 : mTopRowWidget->height();
0204         int bottomRowHeight = mTooManyChangesFrame->isVisibleTo(mSaveBarWidget) ? mTooManyChangesFrame->sizeHint().height() : 0;
0205 
0206         int height = 2 * layout->contentsMargins().top() + layout->contentsMargins().bottom() + topRowHeight + bottomRowHeight;
0207         if (topRowHeight > 0 && bottomRowHeight > 0) {
0208             height += layout->spacing();
0209         }
0210         mSaveBarWidget->setFixedHeight(height);
0211     }
0212 };
0213 
0214 SaveBar::SaveBar(QWidget *parent, KActionCollection *actionCollection)
0215     : SlideContainer(parent)
0216     , d(new SaveBarPrivate)
0217 {
0218     d->q = this;
0219     d->mActionCollection = actionCollection;
0220     d->mSaveBarWidget = new QWidget();
0221     d->mSaveBarWidget->setObjectName(QStringLiteral("saveBarWidget"));
0222     d->applyNormalStyleSheet();
0223 
0224     d->mMessageLabel = new QLabel;
0225     d->mMessageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
0226 
0227     d->mUndoButton = createToolButton();
0228     d->mRedoButton = createToolButton();
0229     d->mSaveCurrentUrlButton = createToolButton();
0230     d->mSaveAsButton = createToolButton();
0231     d->mSaveAllButton = createToolButton();
0232 
0233     d->mActionsLabel = new QLabel;
0234     d->mActionsLabel->setAlignment(Qt::AlignCenter);
0235     d->mActionsLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
0236 
0237     d->createTooManyChangesFrame();
0238 
0239     // Setup top row
0240     d->mTopRowWidget = new QWidget;
0241     auto rowLayout = new QHBoxLayout(d->mTopRowWidget);
0242     rowLayout->addWidget(d->mMessageLabel);
0243     rowLayout->setStretchFactor(d->mMessageLabel, 1);
0244     rowLayout->addWidget(d->mUndoButton);
0245     rowLayout->addWidget(d->mRedoButton);
0246     rowLayout->addWidget(d->mActionsLabel);
0247     rowLayout->addWidget(d->mSaveCurrentUrlButton);
0248     rowLayout->addWidget(d->mSaveAsButton);
0249     rowLayout->addWidget(d->mSaveAllButton);
0250     rowLayout->setContentsMargins(0, 0, 0, 0);
0251 
0252     // Setup bottom row
0253     auto bottomRowLayout = new QHBoxLayout;
0254     bottomRowLayout->addStretch();
0255     bottomRowLayout->addWidget(d->mTooManyChangesFrame);
0256     bottomRowLayout->addStretch();
0257 
0258     // Gather everything together
0259     auto layout = new QVBoxLayout(d->mSaveBarWidget);
0260     layout->addWidget(d->mTopRowWidget);
0261     layout->addLayout(bottomRowLayout);
0262     layout->setContentsMargins(3, 3, 3, 3);
0263     layout->setSpacing(3);
0264 
0265     setContent(d->mSaveBarWidget);
0266 
0267     connect(DocumentFactory::instance(), &DocumentFactory::modifiedDocumentListChanged, this, &SaveBar::updateContent);
0268 
0269     connect(d->mActionsLabel, &QLabel::linkActivated, this, &SaveBar::triggerAction);
0270 }
0271 
0272 SaveBar::~SaveBar()
0273 {
0274     delete d;
0275 }
0276 
0277 void SaveBar::initActionDependentWidgets()
0278 {
0279     d->mUndoButton->setDefaultAction(d->mActionCollection->action(QStringLiteral("edit_undo")));
0280     d->mRedoButton->setDefaultAction(d->mActionCollection->action(QStringLiteral("edit_redo")));
0281     d->mSaveCurrentUrlButton->setDefaultAction(d->mActionCollection->action(QStringLiteral("file_save")));
0282     d->mSaveAsButton->setDefaultAction(d->mActionCollection->action(QStringLiteral("file_save_as")));
0283 
0284     // FIXME: Not using an action for now
0285     d->mSaveAllButton->setText(i18n("Save All"));
0286     d->mSaveAllButton->setToolTip(i18nc("@info:tooltip", "Save all modified images"));
0287     d->mSaveAllButton->setIcon(QIcon::fromTheme(QStringLiteral("document-save-all")));
0288     connect(d->mSaveAllButton, &QToolButton::clicked, this, &SaveBar::requestSaveAll);
0289 
0290     d->mSaveAllFullScreenButton->setText(i18n("Save All"));
0291     connect(d->mSaveAllFullScreenButton, &QToolButton::clicked, this, &SaveBar::requestSaveAll);
0292 
0293     int height = d->mUndoButton->sizeHint().height();
0294     d->mTopRowWidget->setFixedHeight(height);
0295     d->updateWidgetSizes();
0296 }
0297 
0298 void SaveBar::setFullScreenMode(bool isFullScreen)
0299 {
0300     d->mSaveAllFullScreenButton->setVisible(isFullScreen);
0301     if (isFullScreen) {
0302         d->applyFullScreenStyleSheet();
0303     } else {
0304         d->applyNormalStyleSheet();
0305     }
0306     updateContent();
0307 }
0308 
0309 void SaveBar::updateContent()
0310 {
0311     QList<QUrl> lst = DocumentFactory::instance()->modifiedDocumentList();
0312 
0313     if (window()->isFullScreen()) {
0314         d->mTopRowWidget->hide();
0315     } else {
0316         d->mTopRowWidget->show();
0317         d->updateTopRowWidget(lst);
0318     }
0319 
0320     d->updateTooManyChangesFrame(lst);
0321 
0322     d->updateWidgetSizes();
0323     if (lst.isEmpty() || (window()->isFullScreen() && !d->mTooManyChangesFrame->isVisibleTo(d->mSaveBarWidget))) {
0324         slideOut();
0325     } else {
0326         slideIn();
0327     }
0328 }
0329 
0330 void SaveBar::triggerAction(const QString &action)
0331 {
0332     QList<QUrl> lst = DocumentFactory::instance()->modifiedDocumentList();
0333     if (action == QLatin1String("first")) {
0334         Q_EMIT goToUrl(lst[0]);
0335     } else if (action == QLatin1String("previous")) {
0336         int pos = lst.indexOf(d->mCurrentUrl);
0337         --pos;
0338         Q_ASSERT(pos >= 0);
0339         Q_EMIT goToUrl(lst[pos]);
0340     } else if (action == QLatin1String("next")) {
0341         int pos = lst.indexOf(d->mCurrentUrl);
0342         ++pos;
0343         Q_ASSERT(pos < lst.size());
0344         Q_EMIT goToUrl(lst[pos]);
0345     } else {
0346         qCWarning(GWENVIEW_APP_LOG) << "Unknown action: " << action;
0347     }
0348 }
0349 
0350 void SaveBar::setCurrentUrl(const QUrl &url)
0351 {
0352     d->mCurrentUrl = url;
0353     updateContent();
0354 }
0355 
0356 } // namespace
0357 
0358 #include "moc_savebar.cpp"