11#include <QDesktopServices>
15#include <QTemporaryFile>
17#include <QNetworkAccessManager>
19#include <QDomDocument>
28#include "KConfigGroup"
29#include "KSharedConfig"
32#include <QListWidgetItem>
49#include <QCoreApplication>
55#include "config-updaters.h"
65#include <klocalizedstring.h>
90 Q_EMIT setChecked(
true);
100 : QStyledItemDelegate(parent)
109 QSize
sizeHint(
const QStyleOptionViewItem &option,
const QModelIndex &)
const override
111 return QSize(option.rect.width(),
itemHeight);
123 devBuildLabel->setVisible(
false);
124 updaterFrame->setVisible(
false);
125 versionNotificationLabel->setVisible(
false);
126 bnVersionUpdate->setVisible(
false);
127 bnErrorDetails->setVisible(
false);
130 recentDocumentsListView->setDragEnabled(
false);
131 recentDocumentsListView->viewport()->setAutoFillBackground(
false);
132 recentDocumentsListView->setSpacing(2);
133 recentDocumentsListView->installEventFilter(
this);
134 recentDocumentsListView->setViewMode(QListView::IconMode);
135 recentDocumentsListView->setSelectionMode(QAbstractItemView::NoSelection);
141 recentDocumentsListView->setVerticalScrollMode(QListView::ScrollPerPixel);
142 recentDocumentsListView->verticalScrollBar()->setSingleStep(50);
149 recentDocumentsListView->setContextMenuPolicy(Qt::CustomContextMenu);
153 QMenu *newsOptionsMenu =
new QMenu(
this);
154 newsOptionsMenu->setToolTipsVisible(
true);
156 newsOptionsMenu->addAction(showNewsAction);
157 showNewsAction->setToolTip(i18n(
"Show news about Krita: this needs internet to retrieve information from the krita.org website"));
158 showNewsAction->setCheckable(
true);
160 newsOptionsMenu->addSection(i18n(
"Language"));
161 QAction *newsInfoAction = newsOptionsMenu->addAction(i18n(
"English news is always up to date."));
162 newsInfoAction->setEnabled(
false);
165 btnNewsOptions->setMenu(newsOptionsMenu);
170 connect(showNewsAction, SIGNAL(toggled(
bool)), newsWidget, SLOT(setVisible(
bool)));
171 connect(showNewsAction, SIGNAL(toggled(
bool)), labelNoFeed, SLOT(setHidden(
bool)));
172 connect(showNewsAction, SIGNAL(toggled(
bool)), newsWidget, SLOT(toggleNews(
bool)));
173 connect(labelNoFeed, SIGNAL(linkActivated(QString)), showNewsAction, SLOT(enableFromLink(QString)));
175 labelNoFeed->setDismissable(
false);
177#ifdef ENABLE_UPDATERS
178 connect(showNewsAction, SIGNAL(toggled(
bool)),
this, SLOT(slotToggleUpdateChecks(
bool)));
181 donationLink->hide();
182 supporterBadge->hide();
192#ifdef ENABLE_UPDATERS
202#if defined Q_OS_LINUX
203 if (!qEnvironmentVariableIsSet(
"SteamAppId")) {
204 if (qEnvironmentVariableIsSet(
"APPIMAGE")) {
210#elif defined Q_OS_WIN
221 if (!m_versionUpdater.isNull()) {
222 connect(bnVersionUpdate, SIGNAL(clicked()),
this, SLOT(slotRunVersionUpdate()));
223 connect(bnErrorDetails, SIGNAL(clicked()),
this, SLOT(slotShowUpdaterErrorDetails()));
228 m_versionUpdater->checkForUpdate();
240 setAcceptDrops(
true);
254 newFileLinkShortcut->setText(
258 openFileShortcut->setText(
266 connect(clearRecentFilesLink, SIGNAL(clicked(
bool)), mainWin, SLOT(clearRecentFiles()));
275 recentDocumentsListView->setModel(&recentFilesModel->
model());
284 QString dropFrameStyle = QStringLiteral(
"QFrame#dropAreaIndicator { border: 2px solid transparent }");
285 dropFrameBorder->setStyleSheet(dropFrameStyle);
287 QColor
textColor = qApp->palette().color(QPalette::Text);
292 QString dropFrameStyle = QString(
"QFrame#dropAreaIndicator { border: 2px dotted ").append(
blendedColor.name()).append(
" }") ;
293 dropFrameBorder->setStyleSheet(dropFrameStyle);
299 textColor = qApp->palette().color(QPalette::Text);
326 dropFrameBorder->setObjectName(
"dropAreaIndicator");
327 QString dropFrameStyle = QString(
"QFrame#dropAreaIndicator { border: 4px dotted ").append(
blendedColor.name()).append(
"}");
328 dropFrameBorder->setStyleSheet(dropFrameStyle);
334 openFileLink->setIconSize(QSize(48, 48));
335 newFileLink->setIconSize(QSize(48, 48));
341 btnNewsOptions->setFlat(
true);
345 userManualIcon->setIcon(linkIcon);
346 gettingStartedIcon->setIcon(linkIcon);
347 userCommunityIcon->setIcon(linkIcon);
348 kritaWebsiteIcon->setIcon(linkIcon);
349 sourceCodeIcon->setIcon(linkIcon);
354 userCommunityLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://krita-artists.org\">")
355 .append(i18n(
"User Community")).append(
"</a>"));
357 gettingStartedLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://docs.krita.org/user_manual/getting_started.html\">")
358 .append(i18n(
"Getting Started")).append(
"</a>"));
360 manualLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://docs.krita.org\">")
361 .append(i18n(
"User Manual")).append(
"</a>"));
363 supportKritaLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://krita.org/support-us/donations?" +
analyticsString +
"donations" +
"\">")
364 .append(i18n(
"Support Krita")).append(
"</a>"));
366 kritaWebsiteLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://www.krita.org?" +
analyticsString +
"marketing-site" +
"\">")
367 .append(i18n(
"Krita Website")).append(
"</a>"));
369 sourceCodeLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://invent.kde.org/graphics/krita\">")
370 .append(i18n(
"Source Code")).append(
"</a>"));
372 poweredByKDELink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://userbase.kde.org/What_is_KDE\">")
373 .append(i18n(
"Powered by KDE")).append(
"</a>"));
375 QString translationNoFeed = i18n(
"You can <a href=\"ignored\" style=\"color: COLOR_PLACEHOLDER; text-decoration: underline;\">enable news</a> from krita.org in various languages with the menu above");
376 labelNoFeed->setText(translationNoFeed.replace(
"COLOR_PLACEHOLDER",
blendedColor.name()));
379 const QString &faintTextStyle =
"QWidget{color: " + faintTextColor.name() +
"}";
380 labelNoRecentDocs->setStyleSheet(faintTextStyle);
381 labelNoFeed->setStyleSheet(faintTextStyle);
384 const QString &frameQss =
"{border: 1px solid " + frameColor.name() +
"}";
385 recentDocsStackedWidget->setStyleSheet(
"QStackedWidget#recentDocsStackedWidget" + frameQss);
386 newsFrame->setStyleSheet(
"QFrame#newsFrame" + frameQss);
391#ifdef ENABLE_UPDATERS
392 updateVersionUpdaterFrame();
396 donationLink->setText(
397 QStringLiteral(
"<a href=\"#\">%1</a>").arg(QString(i18n(
"Get your Krita Supporter Badge here!"))));
403 supportKritaLink->hide();
404 supportKritaIcon->hide();
405 labelSupportText->hide();
406 kritaWebsiteLink->hide();
407 kritaWebsiteIcon->hide();
408 donationLink->hide();
416 if (event->mimeData()->hasUrls() ||
417 event->mimeData()->hasFormat(
"application/x-krita-node-internal-pointer") ||
418 event->mimeData()->hasFormat(
"application/x-qt-image")) {
419 return event->accept();
422 return event->ignore();
429 if (event->mimeData()->hasUrls() && !event->mimeData()->urls().empty()) {
430 Q_FOREACH (
const QUrl &url, event->mimeData()->urls()) {
431 if (url.toLocalFile().endsWith(
".bundle", Qt::CaseInsensitive)) {
434 qWarning() <<
"Could not install bundle" << url.toLocalFile();
436 }
else if (!url.isLocalFile()) {
437 QScopedPointer<QTemporaryFile>
tmp(
new QTemporaryFile());
438 tmp->setFileName(url.fileName());
443 qWarning() <<
"Fetching" << url <<
"failed";
446 const auto localUrl = QUrl::fromLocalFile(
tmp->fileName());
460 if (event->mimeData()->hasUrls() ||
461 event->mimeData()->hasFormat(
"application/x-krita-node-internal-pointer") ||
462 event->mimeData()->hasFormat(
"application/x-qt-image")) {
463 return event->accept();
466 return event->ignore();
477 if (event->type() == QEvent::FontChange) {
485 if (watched == recentDocumentsListView && event->type() == QEvent::Leave) {
486 recentDocumentsListView->clearSelection();
488 return QWidget::eventFilter(watched, event);
493QString getAutoNewsLang()
496 const QStringList uiLangs = KLocalizedString::languages();
497 QString autoNewsLang = uiLangs.first();
498 if (autoNewsLang.isEmpty()) {
500 autoNewsLang = QString(
"en");
501 }
else if (autoNewsLang ==
"ja") {
502 return QString(
"jp");
503 }
else if (autoNewsLang ==
"zh_CN") {
504 return QString(
"zh");
505 }
else if (autoNewsLang ==
"zh_TW") {
506 return QString(
"zh-tw");
507 }
else if (autoNewsLang ==
"zh_HK") {
508 return QString(
"zh-hk");
509 }
else if (autoNewsLang ==
"en" || autoNewsLang ==
"en_US" || autoNewsLang ==
"en_GB") {
510 return QString(
"en");
526 const QString siteCode;
529 static const std::array<Lang, 22> newsLangs = {{
530 {QString(
"en"), QStringLiteral(
"English")},
531 {QString(
"jp"), QStringLiteral(
"日本語")},
532 {QString(
"zh"), QStringLiteral(
"中文 (简体)")},
533 {QString(
"zh-tw"), QStringLiteral(
"中文 (台灣正體)")},
534 {QString(
"zh-hk"), QStringLiteral(
"廣東話 (香港)")},
535 {QString(
"ca"), QStringLiteral(
"Català")},
536 {QString(
"ca@valencia"), QStringLiteral(
"Català de Valencia")},
537 {QString(
"cs"), QStringLiteral(
"Čeština")},
538 {QString(
"de"), QStringLiteral(
"Deutsch")},
539 {QString(
"eo"), QStringLiteral(
"Esperanto")},
540 {QString(
"es"), QStringLiteral(
"Español")},
541 {QString(
"eu"), QStringLiteral(
"Euskara")},
542 {QString(
"fr"), QStringLiteral(
"Français")},
543 {QString(
"it"), QStringLiteral(
"Italiano")},
544 {QString(
"lt"), QStringLiteral(
"lietuvių")},
545 {QString(
"nl"), QStringLiteral(
"Nederlands")},
546 {QString(
"pt"), QStringLiteral(
"Português")},
547 {QString(
"sk"), QStringLiteral(
"Slovenský")},
548 {QString(
"sl"), QStringLiteral(
"Slovenski")},
549 {QString(
"sv"), QStringLiteral(
"Svenska")},
550 {QString(
"tr"), QStringLiteral(
"Türkçe")},
551 {QString(
"uk"), QStringLiteral(
"Українська")}
554 static const QString newsLangConfigName = QStringLiteral(
"FetchNewsLanguages");
560 auto languagesList = cfg.
readList<QString>(newsLangConfigName);
561 *enabledNewsLangs = QSet(languagesList.begin(), languagesList.end());
565 if (enabledNewsLangs->isEmpty()) {
566 enabledNewsLangs->insert(QString(getAutoNewsLang()));
569 for (
const auto &lang : newsLangs) {
570 QAction *langItem = newsOptionsMenu->addAction(lang.name);
571 langItem->setCheckable(
true);
574 const QString code = lang.siteCode;
575 connect(langItem, &QAction::toggled, newsWidget, [=](
bool checked) {
576 newsWidget->toggleNewsLanguage(code, checked);
580 if (enabledNewsLangs->contains(code)) {
581 langItem->setChecked(
true);
586 connect(langItem, &QAction::toggled, [=](
bool checked) {
592 enabledNewsLangs->insert(QString(code));
594 enabledNewsLangs->remove(QString(code));
596 cfg.
writeList(newsLangConfigName, enabledNewsLangs->values());
605 QString devBuildLabelText = QString(
"<a style=\"color: " +
607 " \" href=\"https://docs.krita.org/en/untranslatable_pages/triaging_bugs.html?"
609 .append(i18n(
"DEV BUILD")).append(
"</a>");
611 devBuildLabel->setText(devBuildLabelText);
612 devBuildIcon->setVisible(
true);
613 devBuildLabel->setVisible(
true);
615 devBuildIcon->setVisible(
false);
616 devBuildLabel->setVisible(
false);
622 QString fileUrl = index.data(Qt::ToolTipRole).toString();
629 QModelIndex index = recentDocumentsListView->indexAt(pos);
630 QAction *actionForget = 0;
631 if (index.isValid()) {
632 actionForget =
new QAction(i18n(
"Forget \"%1\"", index.data(Qt::DisplayRole).toString()), &contextMenu);
633 contextMenu.addAction(actionForget);
635 QAction *triggered = contextMenu.exec(recentDocumentsListView->mapToGlobal(pos));
637 if (index.isValid() && triggered == actionForget) {
660 const bool modelIsEmpty = recentFilesModel->
model().rowCount() == 0;
663 recentDocsStackedWidget->setCurrentWidget(labelNoRecentDocs);
665 recentDocsStackedWidget->setCurrentWidget(recentDocumentsListView);
667 clearRecentFilesLink->setVisible(!modelIsEmpty);
670#ifdef ENABLE_UPDATERS
671void KisWelcomePageWidget::slotToggleUpdateChecks(
bool state)
673 if (m_versionUpdater.isNull()) {
680 m_versionUpdater->checkForUpdate();
683 updateVersionUpdaterFrame();
685void KisWelcomePageWidget::slotRunVersionUpdate()
687 if (m_versionUpdater.isNull()) {
692 m_versionUpdater->doUpdate();
696void KisWelcomePageWidget::slotSetUpdateStatus(
KisUpdaterStatus updateStatus)
698 m_updaterStatus = updateStatus;
699 updateVersionUpdaterFrame();
702void KisWelcomePageWidget::slotShowUpdaterErrorDetails()
704 QMessageBox::warning(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), m_updaterStatus.updaterOutput());
707void KisWelcomePageWidget::updateVersionUpdaterFrame()
709 updaterFrame->setVisible(
false);
710 versionNotificationLabel->setVisible(
false);
711 bnVersionUpdate->setVisible(
false);
712 bnErrorDetails->setVisible(
false);
718 QString versionLabelText;
721 updaterFrame->setVisible(
true);
722 updaterFrame->setEnabled(
true);
723 versionLabelText = i18n(
"New version of Krita is available.");
724 versionNotificationLabel->setVisible(
true);
727 if (m_versionUpdater->hasUpdateCapability()) {
728 bnVersionUpdate->setVisible(
true);
731 QString downloadLink = QString(
" <a style=\"color: %1; text-decoration: underline\" href=\"%2?%3\">Download Krita %4</a>")
733 .arg(m_updaterStatus.downloadLink())
735 .arg(m_updaterStatus.availableVersion());
737 versionLabelText.append(downloadLink);
748 updaterFrame->setVisible(
false);
751 updaterFrame->setVisible(
true);
752 versionLabelText = i18n(
"An error occurred during the update");
753 versionNotificationLabel->setVisible(
true);
754 bnErrorDetails->setVisible(
true);
757 updaterFrame->setVisible(
true);
758 versionLabelText = QString(
"<b>%1</b> %2").arg(i18n(
"Restart is required.")).arg(m_updaterStatus.details());
759 versionNotificationLabel->setVisible(
true);
763 versionNotificationLabel->setText(versionLabelText);
771void KisWelcomePageWidget::initDonations()
774 if (!androidDonations) {
775 qWarning(
"KisWelcomePage::initDonations: androidDonations is null");
779 connect(donationLink, SIGNAL(linkActivated(QString)), androidDonations, SLOT(slotStartDonationFlow()));
781 QString bannerPath = QStandardPaths::locate(QStandardPaths::AppDataLocation,
"share/krita/donation/banner.png");
782 QPixmap
pixmap(bannerPath);
784 qWarning(
"KisWelcomePage::initDonations: failed to load banner from '%s'", qUtf8Printable(bannerPath));
786 supporterBadge->setPixmap(QPixmap(bannerPath));
789 connect(androidDonations, SIGNAL(sigStateChanged()),
this, SLOT(slotUpdateDonationState()));
790 slotUpdateDonationState();
793void KisWelcomePageWidget::slotUpdateDonationState()
795 bool linkVisible =
false;
796 bool badgeVisible =
false;
799 if (androidDonations) {
803 qWarning(
"KisWelcomePageWidget::slotUpdateDonationState: android donations is null");
806 donationLink->setVisible(linkVisible);
807 supporterBadge->setVisible(badgeVisible);
813 QFont larger = font();
814 larger.setPointSizeF(larger.pointSizeF() * 1.1f);
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisAction * actionByName(const QString &name) const
bool shouldShowDonationLink() const
bool shouldShowSupporterBadge() const
static KisAndroidDonations * instance()
void writeList(const QString &name, const QList< T > &value)
QList< T > readList(const QString &name, const QList< T > &defaultValue=QList< T >())
T readEntry(const QString &name, const T &defaultValue=T())
bool installBundle(const QString &fileName) const
Copy the given file into the bundle directory.
bool openDocument(const QString &path, OpenFlags flags)
KisViewManager * viewManager
void slotFileOpen(bool isImporting=false)
void removeRecentFile(QString url)
void dragMoveEvent(QDragMoveEvent *event) override
static constexpr const int ICON_SIZE_LENGTH
QStandardItemModel & model()
static KisRecentDocumentsModelWrapper * instance()
The KisRemoteFileFetcher class can fetch a remote file and blocks until the file is downloaded.
bool fetchFile(const QUrl &remote, QIODevice *io)
fetch the image. Shows a progress dialog
static void log(const QString &message)
Logs with date/time.
KisActionManager * actionManager() const
void recentDocumentClicked(QModelIndex index)
KisWelcomePageWidget(QWidget *parent)
void dragEnterEvent(QDragEnterEvent *event) override
void setupNewsLangSelection(QMenu *newsOptionMenu)
bool isDevelopmentBuild()
void slotNewFileClicked()
bool eventFilter(QObject *watched, QEvent *event) override
void dropEvent(QDropEvent *event) override
void slotUpdateThemeColors()
void slotScrollerStateChanged(QScroller::State state)
void slotOpenFileClicked()
void dragLeaveEvent(QDragLeaveEvent *event) override
void changeEvent(QEvent *event) override
KisMainWindow * m_mainWindow
void showDevVersionHighlight()
void slotRecentFilesModelIsUpToDate()
const QString analyticsString
void setMainWindow(KisMainWindow *m_mainWindow)
void slotRecentDocContextMenuRequest(const QPoint &pos)
void showDropAreaIndicator(bool show)
~KisWelcomePageWidget() override
void dragMoveEvent(QDragMoveEvent *event) override
RecentItemDelegate(QObject *parent=0)
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const override
void setItemHeight(int itemHeight)
void enableFromLink(QString unused_url)
QIcon loadIcon(const QString &name)
void updateIcon(QAbstractButton *button)
QColor blendColors(const QColor &c1, const QColor &c2, qreal r1)
bool isRunningInPackage()
KRITAVERSION_EXPORT bool isDevelopersBuild()