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

0001 /* This file is part of KimageShop^WKrayon^WKrita
0002  *
0003  *  SPDX-FileCopyrightText: 2006 Boudewijn Rempt <boud@valdyas.org>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_statusbar.h"
0009 
0010 #include <QLabel>
0011 #include <QFontMetrics>
0012 #include <QToolButton>
0013 #include <QPushButton>
0014 #include <QAction>
0015 #include <QToolTip>
0016 #include <QStatusBar>
0017 
0018 #include <ksqueezedtextlabel.h>
0019 #include <klocalizedstring.h>
0020 #include <kformat.h>
0021 
0022 #include <KoColorProfile.h>
0023 #include <KoColorSpace.h>
0024 #include <KoToolManager.h>
0025 #include <KoViewConverter.h>
0026 #include <QHBoxLayout>
0027 
0028 #include <KisUsageLogger.h>
0029 
0030 #include <kis_icon_utils.h>
0031 
0032 #include <kis_types.h>
0033 #include <kis_image.h>
0034 #include <kis_selection.h>
0035 #include <kis_paint_device.h>
0036 #include <kis_selection_manager.h>
0037 #include "kis_memory_statistics_server.h"
0038 
0039 #include "KisView.h"
0040 #include "KisDocument.h"
0041 #include "KisViewManager.h"
0042 #include "canvas/kis_canvas2.h"
0043 #include "kis_progress_widget.h"
0044 #include "kis_zoom_manager.h"
0045 #include <KisAngleSelector.h>
0046 #include <kis_canvas_controller.h>
0047 #include <kis_signals_blocker.h>
0048 
0049 #include "KisMainWindow.h"
0050 #include "kis_config.h"
0051 
0052 #include "widgets/KisMemoryReportButton.h"
0053 
0054 enum {
0055     IMAGE_SIZE_ID,
0056     POINTER_POSITION_ID
0057 };
0058 
0059 KisStatusBar::KisStatusBar(KisViewManager *viewManager)
0060     : m_viewManager(viewManager)
0061     , m_imageView(0)
0062     , m_statusBar(0)
0063 {
0064 }
0065 
0066 void KisStatusBar::setup()
0067 {
0068     m_selectionStatus = new QToolButton();
0069     m_selectionStatus->setObjectName("selection status");
0070     m_selectionStatus->setIconSize(QSize(16,16));
0071     m_selectionStatus->setAutoRaise(true);
0072     m_selectionStatus->setEnabled(false);
0073     updateSelectionIcon();
0074 
0075     m_statusBar = m_viewManager->mainWindow()->statusBar();
0076 
0077     connect(m_selectionStatus, SIGNAL(clicked()), m_viewManager->selectionManager(), SLOT(slotToggleSelectionDecoration()));
0078     connect(m_viewManager->selectionManager(), SIGNAL(displaySelectionChanged()), SLOT(updateSelectionToolTip()));
0079     connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), this, SLOT(updateSelectionIcon()));
0080 
0081     addStatusBarItem(m_selectionStatus);
0082     m_selectionStatus->setVisible(false);
0083 
0084 #ifdef Q_OS_ANDROID
0085     m_fullscreenToggle = new QToolButton;
0086     m_fullscreenToggle->setObjectName("Toggle Fullscreen");
0087     m_fullscreenToggle->setCheckable(false);
0088     m_fullscreenToggle->setToolTip(i18n("Toggle Fullscreen"));
0089     m_fullscreenToggle->setAutoRaise(true);
0090     m_fullscreenToggle->setIcon(KisIconUtils::loadIcon("view-fullscreen"));
0091     addStatusBarItem(m_fullscreenToggle);
0092     m_fullscreenToggle->setVisible(true);
0093     connect(m_fullscreenToggle, SIGNAL(clicked()), m_viewManager, SLOT(slotToggleFullscreen()));
0094 #endif
0095 
0096     m_statusBarStatusLabel = new KSqueezedTextLabel();
0097     m_statusBarStatusLabel->setObjectName("statsBarStatusLabel");
0098     m_statusBarStatusLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
0099     m_statusBarStatusLabel->setContentsMargins(5, 5, 5, 5);
0100     connect(KoToolManager::instance(), SIGNAL(changedStatusText(QString)),
0101             m_statusBarStatusLabel, SLOT(setText(QString)));
0102     addStatusBarItem(m_statusBarStatusLabel, 2);
0103     m_statusBarStatusLabel->setVisible(false);
0104 
0105     m_statusBarProfileLabel = new KSqueezedTextLabel();
0106     m_statusBarProfileLabel->setObjectName("statsBarProfileLabel");
0107     m_statusBarProfileLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
0108     m_statusBarProfileLabel->setContentsMargins(5, 5, 5, 5);
0109     addStatusBarItem(m_statusBarProfileLabel, 3);
0110     m_statusBarProfileLabel->setVisible(false);
0111 
0112     m_progress = new KisProgressWidget();
0113     m_progress->setObjectName("ProgressBar");
0114     addStatusBarItem(m_progress);
0115     m_progress->setVisible(false);
0116     connect(m_progress, SIGNAL(sigCancellationRequested()), this, SIGNAL(sigCancellationRequested()));
0117 
0118     m_progressUpdater.reset(new KisProgressUpdater(m_progress, m_progress->progressProxy()));
0119     m_progressUpdater->setAutoNestNames(true);
0120 
0121     m_extraWidgetsParent = new QFrame;
0122     m_extraWidgetsParent->setMinimumWidth(50);
0123     m_extraWidgetsParent->setObjectName("Extra Widgets Parent");
0124     m_extraWidgetsLayout = new QHBoxLayout;
0125     m_extraWidgetsLayout->setContentsMargins(0, 0, 0, 0);
0126     m_extraWidgetsLayout->setObjectName("Extra Widgets Layout");
0127     m_extraWidgetsParent->setLayout(m_extraWidgetsLayout);
0128     addStatusBarItem(m_extraWidgetsParent);
0129 
0130     m_memoryReportBox = new KisMemoryReportButton();
0131     m_memoryReportBox->setObjectName("memoryReportBox");
0132     m_memoryReportBox->setFlat(true);
0133     m_memoryReportBox->setContentsMargins(5, 5, 5, 5);
0134     m_memoryReportBox->setMinimumWidth(120);
0135     addStatusBarItem(m_memoryReportBox);
0136     m_memoryReportBox->setVisible(false);
0137 
0138     connect(m_memoryReportBox, SIGNAL(clicked()), SLOT(showMemoryInfoToolTip()));
0139 
0140     connect(KisMemoryStatisticsServer::instance(),
0141             SIGNAL(sigUpdateMemoryStatistics()),
0142             SLOT(imageSizeChanged()));
0143 
0144     m_canvasAngleSelector = new KisAngleSelector;
0145     m_canvasAngleSelector->setRange(-360.00, 360.0);
0146     m_canvasAngleSelector->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
0147     m_canvasAngleSelector->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_ContextMenu);
0148     m_canvasAngleSelector->useFlatSpinBox(true);
0149     addStatusBarItem(m_canvasAngleSelector);
0150 
0151     connect(m_canvasAngleSelector, SIGNAL(angleChanged(qreal)), SLOT(slotCanvasAngleSelectorAngleChanged(qreal)));
0152     m_canvasAngleSelector->setVisible(false);
0153 }
0154 
0155 KisStatusBar::~KisStatusBar()
0156 {
0157 }
0158 
0159 void KisStatusBar::setView(QPointer<KisView> imageView)
0160 {
0161     if (m_imageView) {
0162         if (m_imageView->canvasBase()) {
0163             m_imageView->canvasBase()->canvasController()->proxyObject->disconnect(this);
0164         }
0165         m_imageView->disconnect(this);
0166         removeStatusBarItem(m_imageView->zoomManager()->zoomActionWidget());
0167         m_imageView = 0;
0168     }
0169 
0170     if (imageView) {
0171         m_imageView = imageView;
0172         m_canvasAngleSelector->setVisible(true);
0173         connect(m_imageView, SIGNAL(sigColorSpaceChanged(const KoColorSpace*)),
0174                 this, SLOT(updateStatusBarProfileLabel()));
0175         connect(m_imageView, SIGNAL(sigProfileChanged(const KoColorProfile*)),
0176                 this, SLOT(updateStatusBarProfileLabel()));
0177         connect(m_imageView, SIGNAL(sigSizeChanged(QPointF,QPointF)),
0178                 this, SLOT(imageSizeChanged()));
0179         connect(m_imageView->canvasController()->proxyObject, SIGNAL(canvasOffsetXChanged(int)),
0180                 this, SLOT(slotCanvasRotationChanged()));
0181         updateStatusBarProfileLabel();
0182         slotCanvasRotationChanged();
0183         addStatusBarItem(m_imageView->zoomManager()->zoomActionWidget());
0184     }
0185     else {
0186         m_canvasAngleSelector->setVisible(false);
0187     }
0188 
0189     imageSizeChanged();
0190 }
0191 
0192 void KisStatusBar::addStatusBarItem(QWidget *widget, int stretch, bool permanent)
0193 {
0194     StatusBarItem sbItem(widget);
0195     if (permanent) {
0196         m_statusBar->addPermanentWidget(widget, stretch);
0197     }
0198     else {
0199         m_statusBar->addWidget(widget, stretch);
0200     }
0201     widget->setVisible(true);
0202     m_statusBarItems.append(sbItem);
0203 }
0204 
0205 void KisStatusBar::removeStatusBarItem(QWidget *widget)
0206 {
0207     int i = 0;
0208     Q_FOREACH(const StatusBarItem& sbItem, m_statusBarItems) {
0209         if (sbItem.widget() == widget) {
0210             break;
0211         }
0212         i++;
0213     }
0214 
0215     if (i < m_statusBarItems.count()) {
0216         m_statusBar->removeWidget(m_statusBarItems[i].widget());
0217         m_statusBarItems.remove(i);
0218     }
0219 }
0220 
0221 void KisStatusBar::hideAllStatusBarItems()
0222 {
0223     Q_FOREACH(const StatusBarItem& sbItem, m_statusBarItems) {
0224         sbItem.hide();
0225     }
0226 }
0227 
0228 void KisStatusBar::showAllStatusBarItems()
0229 {
0230     Q_FOREACH(const StatusBarItem& sbItem, m_statusBarItems) {
0231         sbItem.show();
0232     }
0233 }
0234 
0235 
0236 void KisStatusBar::imageSizeChanged()
0237 {
0238     updateMemoryStatus();
0239 
0240     QString sizeText;
0241     KisImageWSP image = m_imageView ? m_imageView->image() : 0;
0242     if (image) {
0243         qint32 w = image->width();
0244         qint32 h = image->height();
0245         sizeText = i18nc("@info:status width x height (file size)", "%1 &x %2 (%3)", w, h, m_shortMemoryTag);
0246     } else {
0247         sizeText = m_shortMemoryTag;
0248     }
0249 
0250     m_memoryReportBox->setIcon(m_memoryStatusIcon);
0251     m_memoryReportBox->setText(sizeText);
0252     m_memoryReportBox->setToolTip(m_longMemoryTag);
0253 }
0254 
0255 void KisStatusBar::updateSelectionIcon()
0256 {
0257     QIcon icon;
0258     if (!m_viewManager->selectionManager()->displaySelection()) {
0259         icon = KisIconUtils::loadIcon("selection-mode_invisible");
0260     } else if (m_viewManager->selectionManager()->showSelectionAsMask()) {
0261         icon = KisIconUtils::loadIcon("selection-mode_mask");
0262     } else /* if (!m_view->selectionManager()->showSelectionAsMask()) */ {
0263         icon = KisIconUtils::loadIcon("selection-mode_ants");
0264     }
0265     m_selectionStatus->setIcon(icon);
0266 }
0267 
0268 void KisStatusBar::updateMemoryStatus()
0269 {
0270     KisMemoryStatisticsServer::Statistics stats =
0271             KisMemoryStatisticsServer::instance()
0272             ->fetchMemoryStatistics(m_imageView ? m_imageView->image() : 0);
0273     const KFormat format;
0274 
0275     const QString imageStatsMsg =
0276             i18nc("tooltip on statusbar memory reporting button (image stats)",
0277                   "Image size:\t %1\n"
0278                   "  - layers:\t\t %2\n"
0279                   "  - projections:\t %3\n"
0280                   "  - instant preview:\t %4\n",
0281                   format.formatByteSize(stats.imageSize),
0282                   format.formatByteSize(stats.layersSize),
0283                   format.formatByteSize(stats.projectionsSize),
0284                   format.formatByteSize(stats.lodSize));
0285 
0286     const QString memoryStatsMsg =
0287             i18nc("tooltip on statusbar memory reporting button (total stats)",
0288                   "Memory used:\t %1 / %2\n"
0289                   "  image data:\t %3 / %4\n"
0290                   "  pool:\t\t %5 / %6\n"
0291                   "  undo data:\t %7\n"
0292                   "\n"
0293                   "Swap used:\t %8",
0294                   format.formatByteSize(stats.totalMemorySize),
0295                   format.formatByteSize(stats.totalMemoryLimit),
0296 
0297                   format.formatByteSize(stats.realMemorySize),
0298                   format.formatByteSize(stats.tilesHardLimit),
0299 
0300                   format.formatByteSize(stats.poolSize),
0301                   format.formatByteSize(stats.tilesPoolLimit),
0302 
0303                   format.formatByteSize(stats.historicalMemorySize),
0304                   format.formatByteSize(stats.swapSize));
0305 
0306     QString longStats = imageStatsMsg + "\n" + memoryStatsMsg;
0307 
0308     QString shortStats = format.formatByteSize(stats.imageSize);
0309     QIcon icon;
0310     const qint64 warnLevel = stats.tilesHardLimit - stats.tilesHardLimit / 8;
0311 
0312     if (stats.imageSize > warnLevel ||
0313             stats.realMemorySize > warnLevel) {
0314 
0315         if (!m_memoryWarningLogged) {
0316             m_memoryWarningLogged = true;
0317             KisUsageLogger::log(QString("WARNING: %1 is running out of memory:%2\n").arg(m_imageView->document()->path()).arg(longStats));
0318         }
0319 
0320         icon = KisIconUtils::loadIcon("warning");
0321         QString suffix =
0322                 i18nc("tooltip on statusbar memory reporting button",
0323                       "\n\nWARNING:\tOut of memory! Swapping has been started.\n"
0324                       "\t\tPlease configure more RAM for Krita in Settings dialog");
0325         longStats += suffix;
0326 
0327 
0328     }
0329 
0330     m_shortMemoryTag = shortStats;
0331     m_longMemoryTag = longStats;
0332     m_memoryStatusIcon = icon;
0333 
0334     m_memoryReportBox->setMaximumMemory(stats.totalMemoryLimit);
0335     m_memoryReportBox->setCurrentMemory(stats.totalMemorySize);
0336     m_memoryReportBox->setImageWeight(stats.imageSize);
0337 }
0338 
0339 void KisStatusBar::showMemoryInfoToolTip()
0340 {
0341     QToolTip::showText(QCursor::pos(), m_memoryReportBox->toolTip(), m_memoryReportBox);
0342 }
0343 
0344 void KisStatusBar::slotCanvasAngleSelectorAngleChanged(qreal angle)
0345 {
0346     KisCanvas2 *canvas = m_viewManager->canvasBase();
0347     if (!canvas) return;
0348 
0349     KisCanvasController *canvasController = dynamic_cast<KisCanvasController*>(canvas->canvasController());
0350     if (canvasController) {
0351         canvasController->rotateCanvas(angle - canvas->rotationAngle());
0352     }
0353 }
0354 
0355 void KisStatusBar::slotCanvasRotationChanged()
0356 {
0357     KisCanvas2 *canvas = m_viewManager->canvasBase();
0358     if (!canvas) return;
0359 
0360     KisSignalsBlocker l(m_canvasAngleSelector);
0361     m_canvasAngleSelector->setAngle(canvas->rotationAngle());
0362 }
0363 
0364 void KisStatusBar::updateSelectionToolTip()
0365 {
0366     updateSelectionIcon();
0367 
0368     KisSelectionSP selection = m_viewManager->selection();
0369     if (selection) {
0370         m_selectionStatus->setEnabled(true);
0371 
0372         QRect r = selection->selectedExactRect();
0373 
0374         QString displayMode =
0375                 !m_viewManager->selectionManager()->displaySelection() ?
0376                     i18n("Hidden") :
0377                     (m_viewManager->selectionManager()->showSelectionAsMask() ?
0378                          i18n("Mask") : i18n("Ants"));
0379 
0380         m_selectionStatus->setToolTip(
0381                     i18n("Selection: x = %1 y = %2 width = %3 height = %4\n"
0382                          "Display Mode: %5",
0383                          r.x(), r.y(), r.width(), r.height(), displayMode));
0384     } else {
0385         m_selectionStatus->setEnabled(false);
0386         m_selectionStatus->setToolTip(i18n("No Selection"));
0387     }
0388 }
0389 
0390 void KisStatusBar::setSelection(KisImageWSP image)
0391 {
0392     Q_UNUSED(image);
0393     updateSelectionToolTip();
0394 }
0395 
0396 void KisStatusBar::setProfile(KisImageWSP image)
0397 {
0398     if (m_statusBarProfileLabel == 0) {
0399         return;
0400     }
0401 
0402     if (!image) return;
0403     if (image->profile() == 0) {
0404         m_statusBarProfileLabel->setText(i18n("No profile"));
0405     } else {
0406         m_statusBarProfileLabel->setText(i18nc("<color space> <image profile>", "%1  %2", image->colorSpace()->name(), image->profile()->name()));
0407     }
0408 
0409 }
0410 
0411 void KisStatusBar::setHelp(const QString &t)
0412 {
0413     Q_UNUSED(t);
0414 }
0415 
0416 void KisStatusBar::updateStatusBarProfileLabel()
0417 {
0418     if (!m_imageView) return;
0419 
0420     setProfile(m_imageView->image());
0421 }
0422 
0423 KoProgressUpdater *KisStatusBar::progressUpdater()
0424 {
0425     return m_progressUpdater.data();
0426 }
0427 
0428 void KisStatusBar::addExtraWidget(QWidget *widget)
0429 {
0430     m_extraWidgetsLayout->addWidget(widget);
0431 }
0432 
0433 void KisStatusBar::removeExtraWidget(QWidget *widget)
0434 {
0435     m_extraWidgetsLayout->removeWidget(widget);
0436 }
0437