11#include <QDesktopServices>
15#include <QTemporaryFile>
17#include <QNetworkAccessManager>
19#include <QDomDocument>
29#include "KConfigGroup"
30#include "KSharedConfig"
33#include <QListWidgetItem>
51#include <QCoreApplication>
57#include "config-updaters.h"
67#include <klocalizedstring.h>
92 Q_EMIT setChecked(
true);
102 : QStyledItemDelegate(parent)
111 QSize
sizeHint(
const QStyleOptionViewItem &option,
const QModelIndex &)
const override
113 return QSize(option.rect.width(),
itemHeight);
125 devBuildLabel->setVisible(
false);
126 updaterFrame->setVisible(
false);
127 versionNotificationLabel->setVisible(
false);
128 bnVersionUpdate->setVisible(
false);
129 bnErrorDetails->setVisible(
false);
132 recentDocumentsListView->setDragEnabled(
false);
133 recentDocumentsListView->viewport()->setAutoFillBackground(
false);
134 recentDocumentsListView->setSpacing(2);
135 recentDocumentsListView->installEventFilter(
this);
136 recentDocumentsListView->setViewMode(QListView::IconMode);
137 recentDocumentsListView->setSelectionMode(QAbstractItemView::NoSelection);
143 recentDocumentsListView->setVerticalScrollMode(QListView::ScrollPerPixel);
144 recentDocumentsListView->verticalScrollBar()->setSingleStep(50);
151 recentDocumentsListView->setContextMenuPolicy(Qt::CustomContextMenu);
155 QMenu *newsOptionsMenu =
new QMenu(
this);
156 newsOptionsMenu->setToolTipsVisible(
true);
158 newsOptionsMenu->addAction(showNewsAction);
159 showNewsAction->setToolTip(i18n(
"Show news about Krita: this needs internet to retrieve information from the krita.org website"));
160 showNewsAction->setCheckable(
true);
162 newsOptionsMenu->addSection(i18n(
"Language"));
163 QAction *newsInfoAction = newsOptionsMenu->addAction(i18n(
"English news is always up to date."));
164 newsInfoAction->setEnabled(
false);
167 btnNewsOptions->setMenu(newsOptionsMenu);
172 connect(showNewsAction, SIGNAL(toggled(
bool)), newsWidget, SLOT(setVisible(
bool)));
173 connect(showNewsAction, SIGNAL(toggled(
bool)), labelNoFeed, SLOT(setHidden(
bool)));
174 connect(showNewsAction, SIGNAL(toggled(
bool)), newsWidget, SLOT(toggleNews(
bool)));
175 connect(labelNoFeed, SIGNAL(linkActivated(QString)), showNewsAction, SLOT(enableFromLink(QString)));
177 labelNoFeed->setDismissable(
false);
179#ifdef ENABLE_UPDATERS
180 connect(showNewsAction, SIGNAL(toggled(
bool)),
this, SLOT(slotToggleUpdateChecks(
bool)));
183 donationLink->hide();
184 supporterBadge->hide();
194#ifdef ENABLE_UPDATERS
204#if defined Q_OS_LINUX
205 if (!qEnvironmentVariableIsSet(
"SteamAppId")) {
206 if (qEnvironmentVariableIsSet(
"APPIMAGE")) {
212#elif defined Q_OS_WIN
223 if (!m_versionUpdater.isNull()) {
224 connect(bnVersionUpdate, SIGNAL(clicked()),
this, SLOT(slotRunVersionUpdate()));
225 connect(bnErrorDetails, SIGNAL(clicked()),
this, SLOT(slotShowUpdaterErrorDetails()));
226 connect(m_versionUpdater.data(), SIGNAL(sigUpdateCheckStateChange(
KisUpdaterStatus)),
230 m_versionUpdater->checkForUpdate();
242 setAcceptDrops(
true);
256 newFileLinkShortcut->setText(
260 openFileShortcut->setText(
263 connect(recentDocumentsListView, SIGNAL(clicked(QModelIndex)),
this, SLOT(
recentDocumentClicked(QModelIndex)));
268 connect(clearRecentFilesLink, SIGNAL(clicked(
bool)), mainWin, SLOT(clearRecentFiles()));
271 connect(pasteAction, SIGNAL(triggered()),
this, SLOT(
slotPaste()));
280 recentDocumentsListView->setModel(&recentFilesModel->
model());
289 QString dropFrameStyle = QStringLiteral(
"QFrame#dropAreaIndicator { border: 2px solid transparent }");
290 dropFrameBorder->setStyleSheet(dropFrameStyle);
292 QColor
textColor = qApp->palette().color(QPalette::Text);
297 QString dropFrameStyle = QString(
"QFrame#dropAreaIndicator { border: 2px dotted ").append(
blendedColor.name()).append(
" }") ;
298 dropFrameBorder->setStyleSheet(dropFrameStyle);
304 textColor = qApp->palette().color(QPalette::Text);
331 dropFrameBorder->setObjectName(
"dropAreaIndicator");
332 QString dropFrameStyle = QString(
"QFrame#dropAreaIndicator { border: 4px dotted ").append(
blendedColor.name()).append(
"}");
333 dropFrameBorder->setStyleSheet(dropFrameStyle);
339 openFileLink->setIconSize(QSize(48, 48));
340 newFileLink->setIconSize(QSize(48, 48));
346 btnNewsOptions->setFlat(
true);
350 userManualIcon->setIcon(linkIcon);
351 gettingStartedIcon->setIcon(linkIcon);
352 userCommunityIcon->setIcon(linkIcon);
353 kritaWebsiteIcon->setIcon(linkIcon);
354 sourceCodeIcon->setIcon(linkIcon);
359 userCommunityLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://krita-artists.org\">")
360 .append(i18n(
"User Community")).append(
"</a>"));
362 gettingStartedLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://docs.krita.org/user_manual/getting_started.html\">")
363 .append(i18n(
"Getting Started")).append(
"</a>"));
365 manualLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://docs.krita.org\">")
366 .append(i18n(
"User Manual")).append(
"</a>"));
368 supportKritaLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://krita.org/support-us/donations?" +
analyticsString +
"donations" +
"\">")
369 .append(i18n(
"Support Krita")).append(
"</a>"));
371 kritaWebsiteLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://www.krita.org?" +
analyticsString +
"marketing-site" +
"\">")
372 .append(i18n(
"Krita Website")).append(
"</a>"));
374 sourceCodeLink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://invent.kde.org/graphics/krita\">")
375 .append(i18n(
"Source Code")).append(
"</a>"));
377 poweredByKDELink->setText(QString(
"<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://userbase.kde.org/What_is_KDE\">")
378 .append(i18n(
"Powered by KDE")).append(
"</a>"));
380 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");
381 labelNoFeed->setText(translationNoFeed.replace(
"COLOR_PLACEHOLDER",
blendedColor.name()));
384 const QString &faintTextStyle =
"QWidget{color: " + faintTextColor.name() +
"}";
385 labelNoRecentDocs->setStyleSheet(faintTextStyle);
386 labelNoFeed->setStyleSheet(faintTextStyle);
389 const QString &frameQss =
"{border: 1px solid " + frameColor.name() +
"}";
390 recentDocsStackedWidget->setStyleSheet(
"QStackedWidget#recentDocsStackedWidget" + frameQss);
391 newsFrame->setStyleSheet(
"QFrame#newsFrame" + frameQss);
396#ifdef ENABLE_UPDATERS
397 updateVersionUpdaterFrame();
401 donationLink->setText(
402 QStringLiteral(
"<a href=\"#\">%1</a>").arg(QString(i18n(
"Get your Krita Supporter Badge here!"))));
408 supportKritaLink->hide();
409 supportKritaIcon->hide();
410 labelSupportText->hide();
411 kritaWebsiteLink->hide();
412 kritaWebsiteIcon->hide();
413 donationLink->hide();
421 if (event->mimeData()->hasUrls() ||
422 event->mimeData()->hasFormat(
"application/x-krita-node-internal-pointer") ||
423 event->mimeData()->hasFormat(
"application/x-qt-image")) {
424 return event->accept();
427 return event->ignore();
434 if (event->mimeData()->hasUrls() && !event->mimeData()->urls().empty()) {
435 Q_FOREACH (
const QUrl &url, event->mimeData()->urls()) {
436 if (url.toLocalFile().endsWith(
".bundle", Qt::CaseInsensitive)) {
439 qWarning() <<
"Could not install bundle" << url.toLocalFile();
441 }
else if (!url.isLocalFile()) {
442 QScopedPointer<QTemporaryFile>
tmp(
new QTemporaryFile());
443 tmp->setFileName(url.fileName());
448 qWarning() <<
"Fetching" << url <<
"failed";
451 const auto localUrl = QUrl::fromLocalFile(
tmp->fileName());
465 if (event->mimeData()->hasUrls() ||
466 event->mimeData()->hasFormat(
"application/x-krita-node-internal-pointer") ||
467 event->mimeData()->hasFormat(
"application/x-qt-image")) {
468 return event->accept();
471 return event->ignore();
482 if (event->type() == QEvent::FontChange) {
490 if (watched == recentDocumentsListView && event->type() == QEvent::Leave) {
491 recentDocumentsListView->clearSelection();
493 return QWidget::eventFilter(watched, event);
498QString getAutoNewsLang()
501 const QStringList uiLangs = KLocalizedString::languages();
502 QString autoNewsLang = uiLangs.first();
503 if (autoNewsLang.isEmpty()) {
505 autoNewsLang = QString(
"en");
506 }
else if (autoNewsLang ==
"ja") {
507 return QString(
"jp");
508 }
else if (autoNewsLang ==
"zh_CN") {
509 return QString(
"zh");
510 }
else if (autoNewsLang ==
"zh_TW") {
511 return QString(
"zh-tw");
512 }
else if (autoNewsLang ==
"zh_HK") {
513 return QString(
"zh-hk");
514 }
else if (autoNewsLang ==
"en" || autoNewsLang ==
"en_US" || autoNewsLang ==
"en_GB") {
515 return QString(
"en");
531 const QString siteCode;
534 static const std::array<Lang, 22> newsLangs = {{
535 {QString(
"en"), QStringLiteral(
"English")},
536 {QString(
"jp"), QStringLiteral(
"日本語")},
537 {QString(
"zh"), QStringLiteral(
"中文 (简体)")},
538 {QString(
"zh-tw"), QStringLiteral(
"中文 (台灣正體)")},
539 {QString(
"zh-hk"), QStringLiteral(
"廣東話 (香港)")},
540 {QString(
"ca"), QStringLiteral(
"Català")},
541 {QString(
"ca@valencia"), QStringLiteral(
"Català de Valencia")},
542 {QString(
"cs"), QStringLiteral(
"Čeština")},
543 {QString(
"de"), QStringLiteral(
"Deutsch")},
544 {QString(
"eo"), QStringLiteral(
"Esperanto")},
545 {QString(
"es"), QStringLiteral(
"Español")},
546 {QString(
"eu"), QStringLiteral(
"Euskara")},
547 {QString(
"fr"), QStringLiteral(
"Français")},
548 {QString(
"it"), QStringLiteral(
"Italiano")},
549 {QString(
"lt"), QStringLiteral(
"lietuvių")},
550 {QString(
"nl"), QStringLiteral(
"Nederlands")},
551 {QString(
"pt"), QStringLiteral(
"Português")},
552 {QString(
"sk"), QStringLiteral(
"Slovenský")},
553 {QString(
"sl"), QStringLiteral(
"Slovenski")},
554 {QString(
"sv"), QStringLiteral(
"Svenska")},
555 {QString(
"tr"), QStringLiteral(
"Türkçe")},
556 {QString(
"uk"), QStringLiteral(
"Українська")}
559 static const QString newsLangConfigName = QStringLiteral(
"FetchNewsLanguages");
565 auto languagesList = cfg.
readList<QString>(newsLangConfigName);
566 *enabledNewsLangs = QSet(languagesList.begin(), languagesList.end());
570 if (enabledNewsLangs->isEmpty()) {
571 enabledNewsLangs->insert(QString(getAutoNewsLang()));
574 for (
const auto &lang : newsLangs) {
575 QAction *langItem = newsOptionsMenu->addAction(lang.name);
576 langItem->setCheckable(
true);
579 const QString code = lang.siteCode;
580 connect(langItem, &QAction::toggled, newsWidget, [=](
bool checked) {
581 newsWidget->toggleNewsLanguage(code, checked);
585 if (enabledNewsLangs->contains(code)) {
586 langItem->setChecked(
true);
591 connect(langItem, &QAction::toggled, [=](
bool checked) {
597 enabledNewsLangs->insert(QString(code));
599 enabledNewsLangs->remove(QString(code));
601 cfg.
writeList(newsLangConfigName, enabledNewsLangs->values());
610 QString devBuildLabelText = QString(
"<a style=\"color: " +
612 " \" href=\"https://docs.krita.org/en/untranslatable_pages/triaging_bugs.html?"
614 .append(i18n(
"DEV BUILD")).append(
"</a>");
616 devBuildLabel->setText(devBuildLabelText);
617 devBuildIcon->setVisible(
true);
618 devBuildLabel->setVisible(
true);
620 devBuildIcon->setVisible(
false);
621 devBuildLabel->setVisible(
false);
627 QString fileUrl = index.data(Qt::ToolTipRole).toString();
634 QModelIndex index = recentDocumentsListView->indexAt(pos);
635 QAction *actionForget = 0;
636 if (index.isValid()) {
637 actionForget =
new QAction(i18n(
"Forget \"%1\"", index.data(Qt::DisplayRole).toString()), &contextMenu);
638 contextMenu.addAction(actionForget);
640 QAction *triggered = contextMenu.exec(recentDocumentsListView->mapToGlobal(pos));
642 if (index.isValid() && triggered == actionForget) {
664 if (!this->isVisible())
681 const bool modelIsEmpty = recentFilesModel->
model().rowCount() == 0;
684 recentDocsStackedWidget->setCurrentWidget(labelNoRecentDocs);
686 recentDocsStackedWidget->setCurrentWidget(recentDocumentsListView);
688 clearRecentFilesLink->setVisible(!modelIsEmpty);
691#ifdef ENABLE_UPDATERS
692void KisWelcomePageWidget::slotToggleUpdateChecks(
bool state)
694 if (m_versionUpdater.isNull()) {
701 m_versionUpdater->checkForUpdate();
704 updateVersionUpdaterFrame();
706void KisWelcomePageWidget::slotRunVersionUpdate()
708 if (m_versionUpdater.isNull()) {
713 m_versionUpdater->doUpdate();
717void KisWelcomePageWidget::slotSetUpdateStatus(
KisUpdaterStatus updateStatus)
719 m_updaterStatus = updateStatus;
720 updateVersionUpdaterFrame();
723void KisWelcomePageWidget::slotShowUpdaterErrorDetails()
725 QMessageBox::warning(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), m_updaterStatus.updaterOutput());
728void KisWelcomePageWidget::updateVersionUpdaterFrame()
730 updaterFrame->setVisible(
false);
731 versionNotificationLabel->setVisible(
false);
732 bnVersionUpdate->setVisible(
false);
733 bnErrorDetails->setVisible(
false);
739 QString versionLabelText;
742 updaterFrame->setVisible(
true);
743 updaterFrame->setEnabled(
true);
744 versionLabelText = i18n(
"New version of Krita is available.");
745 versionNotificationLabel->setVisible(
true);
748 if (m_versionUpdater->hasUpdateCapability()) {
749 bnVersionUpdate->setVisible(
true);
752 QString downloadLink = QString(
" <a style=\"color: %1; text-decoration: underline\" href=\"%2?%3\">Download Krita %4</a>")
754 .arg(m_updaterStatus.downloadLink())
756 .arg(m_updaterStatus.availableVersion());
758 versionLabelText.append(downloadLink);
769 updaterFrame->setVisible(
false);
772 updaterFrame->setVisible(
true);
773 versionLabelText = i18n(
"An error occurred during the update");
774 versionNotificationLabel->setVisible(
true);
775 bnErrorDetails->setVisible(
true);
778 updaterFrame->setVisible(
true);
779 versionLabelText = QString(
"<b>%1</b> %2").arg(i18n(
"Restart is required.")).arg(m_updaterStatus.details());
780 versionNotificationLabel->setVisible(
true);
784 versionNotificationLabel->setText(versionLabelText);
792void KisWelcomePageWidget::initDonations()
795 if (!androidDonations) {
796 qWarning(
"KisWelcomePage::initDonations: androidDonations is null");
800 connect(donationLink, SIGNAL(linkActivated(QString)), androidDonations, SLOT(slotStartDonationFlow()));
802 QString bannerPath = QStandardPaths::locate(QStandardPaths::AppDataLocation,
"share/krita/donation/banner.png");
803 QPixmap
pixmap(bannerPath);
805 qWarning(
"KisWelcomePage::initDonations: failed to load banner from '%s'", qUtf8Printable(bannerPath));
807 supporterBadge->setPixmap(QPixmap(bannerPath));
810 connect(androidDonations, SIGNAL(sigStateChanged()),
this, SLOT(slotUpdateDonationState()));
811 slotUpdateDonationState();
814void KisWelcomePageWidget::slotUpdateDonationState()
816 bool linkVisible =
false;
817 bool badgeVisible =
false;
820 if (androidDonations) {
824 qWarning(
"KisWelcomePageWidget::slotUpdateDonationState: android donations is null");
827 donationLink->setVisible(linkVisible);
828 supporterBadge->setVisible(badgeVisible);
834 QFont larger = font();
837 if (larger.pixelSize() == -1) {
838 larger.setPointSizeF(larger.pointSizeF() * ratio);
840 larger.setPixelSize(qRound(larger.pixelSize() * ratio));
KisAction * actionByName(const QString &name) const
bool shouldShowDonationLink() const
bool shouldShowSupporterBadge() const
static KisAndroidDonations * instance()
static KisClipboard * 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())
void SelectPage(Page page)
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()