Krita Source Code Documentation
Loading...
Searching...
No Matches
KisWelcomePageWidget.cpp
Go to the documentation of this file.
1
2/* This file is part of the KDE project
3 * SPDX-FileCopyrightText: 2018 Scott Petrovic <scottpetrovic@gmail.com>
4 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
11#include <QDesktopServices>
12#include <QMimeData>
13#include <QPixmap>
14#include <QMessageBox>
15#include <QTemporaryFile>
16#include <QBuffer>
17#include <QNetworkAccessManager>
18#include <QEventLoop>
19#include <QDomDocument>
20
22#include "kactioncollection.h"
23#include "kis_action.h"
24#include "kis_action_manager.h"
26#include <KisMimeDatabase.h>
27#include <KisApplication.h>
28
29#include "KConfigGroup"
30#include "KSharedConfig"
31
32#include <QListWidget>
33#include <QListWidgetItem>
34#include <QMenu>
35#include <QScrollBar>
36
37#include "kis_clipboard.h"
38#include "kis_icon_utils.h"
39#include <kis_painting_tweaks.h>
40#include "KoStore.h"
41#include "kis_config.h"
42#include "KisDocument.h"
43#include <kis_image.h>
44#include <kis_paint_device.h>
45#include <KisPart.h>
46#include <KisKineticScroller.h>
47#include "KisMainWindow.h"
48
50
51#include <QCoreApplication>
52#include <kis_debug.h>
53#include <QDir>
54
55#include <array>
56
57#include "config-updaters.h"
58
59#ifdef ENABLE_UPDATERS
60#ifdef Q_OS_LINUX
62#endif
63
65#endif
66
67#include <klocalizedstring.h>
68#include <KritaVersionWrapper.h>
69
70#include <KisUsageLogger.h>
71#include <QSysInfo>
72#include <kis_config.h>
73#include <kis_image_config.h>
74#include "opengl/kis_opengl.h"
75
76#ifdef Q_OS_WIN
78#endif
79
80#ifdef Q_OS_MACOS
82#endif
83
84#ifdef Q_OS_ANDROID
85#include "KisAndroidDonations.h"
86#endif
87
88// Used for triggering a QAction::setChecked signal from a QLabel::linkActivated signal
89void ShowNewsAction::enableFromLink(QString unused_url)
90{
91 Q_UNUSED(unused_url);
92 Q_EMIT setChecked(true);
93}
94
95
96// class to override item height for Breeze since qss seems to not work
97class RecentItemDelegate : public QStyledItemDelegate
98{
99 int itemHeight = 0;
100public:
101 RecentItemDelegate(QObject *parent = 0)
102 : QStyledItemDelegate(parent)
103 {
104 }
105
107 {
108 this->itemHeight = itemHeight;
109 }
110
111 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const override
112 {
113 return QSize(option.rect.width(), itemHeight);
114 }
115};
116
117
119 : QWidget(parent)
120{
121 setupUi(this);
122
123 // URLs that go to web browser...
124 devBuildIcon->setIcon(KisIconUtils::loadIcon("warning"));
125 devBuildLabel->setVisible(false);
126 updaterFrame->setVisible(false);
127 versionNotificationLabel->setVisible(false);
128 bnVersionUpdate->setVisible(false);
129 bnErrorDetails->setVisible(false);
130
131 // Recent docs...
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);
138
139// m_recentItemDelegate.reset(new RecentItemDelegate(this));
140// m_recentItemDelegate->setItemHeight(KisRecentDocumentsModelWrapper::ICON_SIZE_LENGTH);
141// recentDocumentsListView->setItemDelegate(m_recentItemDelegate.data());
143 recentDocumentsListView->setVerticalScrollMode(QListView::ScrollPerPixel);
144 recentDocumentsListView->verticalScrollBar()->setSingleStep(50);
145 {
146 QScroller* scroller = KisKineticScroller::createPreconfiguredScroller(recentDocumentsListView);
147 if (scroller) {
148 connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State)));
149 }
150 }
151 recentDocumentsListView->setContextMenuPolicy(Qt::CustomContextMenu);
152 connect(recentDocumentsListView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(slotRecentDocContextMenuRequest(QPoint)));
153
154 // News widget...
155 QMenu *newsOptionsMenu = new QMenu(this);
156 newsOptionsMenu->setToolTipsVisible(true);
157 ShowNewsAction *showNewsAction = new ShowNewsAction(i18n("Enable news and check for new releases"), newsOptionsMenu);
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);
161
162 newsOptionsMenu->addSection(i18n("Language"));
163 QAction *newsInfoAction = newsOptionsMenu->addAction(i18n("English news is always up to date."));
164 newsInfoAction->setEnabled(false);
165
166 setupNewsLangSelection(newsOptionsMenu);
167 btnNewsOptions->setMenu(newsOptionsMenu);
168
169 labelSupportText->setFont(largerFont());
170 donationLink->setFont(largerFont());
171
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)));
176
177 labelNoFeed->setDismissable(false);
178
179#ifdef ENABLE_UPDATERS
180 connect(showNewsAction, SIGNAL(toggled(bool)), this, SLOT(slotToggleUpdateChecks(bool)));
181#endif
182
183 donationLink->hide();
184 supporterBadge->hide();
185#ifdef Q_OS_ANDROID
186 initDonations();
187#endif
188
189 // configure the News area
190 KisConfig cfg(true);
191 m_networkIsAllowed = cfg.readEntry<bool>("FetchNews", false);
192
193
194#ifdef ENABLE_UPDATERS
195#ifndef Q_OS_ANDROID
196 // Setup version updater, but do not check for them, unless the user explicitly
197 // wants to check for updates.
198 // * No updater is created for Linux/Steam, Windows/Steam and Windows/Store distributions,
199 // as those stores have their own updating mechanism.
200 // * STEAMAPPID(Windows)/SteamAppId(Linux) environment variable is set when Krita is run from Steam.
201 // The environment variables are not public API.
202 // * MS Store version runs as a package (though we cannot know if it was
203 // installed from the Store or manually with the .msix package)
204#if defined Q_OS_LINUX
205 if (!qEnvironmentVariableIsSet("SteamAppId")) { // do not create updater for linux/steam
206 if (qEnvironmentVariableIsSet("APPIMAGE")) {
207 m_versionUpdater.reset(new KisAppimageUpdater());
208 } else {
209 m_versionUpdater.reset(new KisManualUpdater());
210 }
211 }
212#elif defined Q_OS_WIN
213 if (!KisWindowsPackageUtils::isRunningInPackage() && !qEnvironmentVariableIsSet("STEAMAPPID")) {
214 m_versionUpdater.reset(new KisManualUpdater());
215 KisUsageLogger::log("Non-store package - creating updater");
216 } else {
217 KisUsageLogger::log("detected appx or steam package - not creating the updater");
218 }
219#else
220 // always create updater for MacOS
221 m_versionUpdater.reset(new KisManualUpdater());
222#endif // Q_OS_*
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)),
227 this, SLOT(slotSetUpdateStatus(const KisUpdaterStatus&)));
228
229 if (m_networkIsAllowed) { // only if the user wants them
230 m_versionUpdater->checkForUpdate();
231 }
232 }
233#endif // ifndef Q_OS_ANDROID
234#endif // ENABLE_UPDATERS
235
236
237 showNewsAction->setChecked(m_networkIsAllowed);
238 newsWidget->setVisible(m_networkIsAllowed);
239 versionNotificationLabel->setEnabled(m_networkIsAllowed);
240
241 // Drop area..
242 setAcceptDrops(true);
243}
244
248
250{
251 if (mainWin) {
252 m_mainWindow = mainWin;
253
254 // set the shortcut links from actions (only if a shortcut exists)
255 if ( mainWin->viewManager()->actionManager()->actionByName("file_new")->shortcut().toString() != "") {
256 newFileLinkShortcut->setText(
257 QString("(") + mainWin->viewManager()->actionManager()->actionByName("file_new")->shortcut().toString(QKeySequence::NativeText) + QString(")"));
258 }
259 if (mainWin->viewManager()->actionManager()->actionByName("file_open")->shortcut().toString() != "") {
260 openFileShortcut->setText(
261 QString("(") + mainWin->viewManager()->actionManager()->actionByName("file_open")->shortcut().toString(QKeySequence::NativeText) + QString(")"));
262 }
263 connect(recentDocumentsListView, SIGNAL(clicked(QModelIndex)), this, SLOT(recentDocumentClicked(QModelIndex)));
264 // we need the view manager to actually call actions, so don't create the connections
265 // until after the view manager is set
266 connect(newFileLink, SIGNAL(clicked(bool)), this, SLOT(slotNewFileClicked()));
267 connect(openFileLink, SIGNAL(clicked(bool)), this, SLOT(slotOpenFileClicked()));
268 connect(clearRecentFilesLink, SIGNAL(clicked(bool)), mainWin, SLOT(clearRecentFiles()));
269
270 KisAction *pasteAction = mainWin->viewManager()->actionManager()->actionByName("edit_paste");
271 connect(pasteAction, SIGNAL(triggered()), this, SLOT(slotPaste()));
272
274
275 // allows RSS news items to apply analytics tracking.
276 newsWidget->setAnalyticsTracking("?" + analyticsString);
277
279 connect(recentFilesModel, SIGNAL(sigModelIsUpToDate()), this, SLOT(slotRecentFilesModelIsUpToDate()));
280 recentDocumentsListView->setModel(&recentFilesModel->model());
282 }
283}
284
285
287{
288 if (!show) {
289 QString dropFrameStyle = QStringLiteral("QFrame#dropAreaIndicator { border: 2px solid transparent }");
290 dropFrameBorder->setStyleSheet(dropFrameStyle);
291 } else {
292 QColor textColor = qApp->palette().color(QPalette::Text);
293 QColor backgroundColor = qApp->palette().color(QPalette::Window);
295
296 // QColor.name() turns it into a hex/web format
297 QString dropFrameStyle = QString("QFrame#dropAreaIndicator { border: 2px dotted ").append(blendedColor.name()).append(" }") ;
298 dropFrameBorder->setStyleSheet(dropFrameStyle);
299 }
300}
301
303{
304 textColor = qApp->palette().color(QPalette::Text);
305 backgroundColor = qApp->palette().color(QPalette::Window);
306
307 // make the welcome screen labels a subtle color so it doesn't clash with the main UI elements
309 // only apply color to the widget itself, not to the tooltip or something
310 blendedStyle = "QWidget{color: " + blendedColor.name() + "}";
311
312 // what labels to change the color...
313 startTitleLabel->setStyleSheet(blendedStyle);
314 recentDocumentsLabel->setStyleSheet(blendedStyle);
315 helpTitleLabel->setStyleSheet(blendedStyle);
316 newsTitleLabel->setStyleSheet(blendedStyle);
317 newFileLinkShortcut->setStyleSheet(blendedStyle);
318 openFileShortcut->setStyleSheet(blendedStyle);
319 clearRecentFilesLink->setStyleSheet(blendedStyle);
320 recentDocumentsListView->setStyleSheet(blendedStyle);
321 newsWidget->setStyleSheet(blendedStyle);
322
323#ifdef Q_OS_ANDROID
324 blendedStyle = blendedStyle + "\nQPushButton { padding: 10px }";
325#endif
326
327 newFileLink->setStyleSheet(blendedStyle);
328 openFileLink->setStyleSheet(blendedStyle);
329
330 // make drop area QFrame have a dotted line
331 dropFrameBorder->setObjectName("dropAreaIndicator");
332 QString dropFrameStyle = QString("QFrame#dropAreaIndicator { border: 4px dotted ").append(blendedColor.name()).append("}");
333 dropFrameBorder->setStyleSheet(dropFrameStyle);
334
335 // only show drop area when we have a document over the empty area
337
338 // add icons for new and open settings to make them stand out a bit more
339 openFileLink->setIconSize(QSize(48, 48));
340 newFileLink->setIconSize(QSize(48, 48));
341
342 openFileLink->setIcon(KisIconUtils::loadIcon("document-open"));
343 newFileLink->setIcon(KisIconUtils::loadIcon("document-new"));
344
345 btnNewsOptions->setIcon(KisIconUtils::loadIcon("view-choose"));
346 btnNewsOptions->setFlat(true);
347
348 supportKritaIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("support-krita")));
349 const QIcon &linkIcon = KisIconUtils::loadIcon(QStringLiteral("bookmarks"));
350 userManualIcon->setIcon(linkIcon);
351 gettingStartedIcon->setIcon(linkIcon);
352 userCommunityIcon->setIcon(linkIcon);
353 kritaWebsiteIcon->setIcon(linkIcon);
354 sourceCodeIcon->setIcon(linkIcon);
355
356 kdeIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("kde")));
357
358 // HTML links seem to be a bit more stubborn with theme changes... setting inline styles to help with color change
359 userCommunityLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://krita-artists.org\">")
360 .append(i18n("User Community")).append("</a>"));
361
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>"));
364
365 manualLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://docs.krita.org\">")
366 .append(i18n("User Manual")).append("</a>"));
367
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>"));
370
371 kritaWebsiteLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://www.krita.org?" + analyticsString + "marketing-site" + "\">")
372 .append(i18n("Krita Website")).append("</a>"));
373
374 sourceCodeLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://invent.kde.org/graphics/krita\">")
375 .append(i18n("Source Code")).append("</a>"));
376
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>"));
379
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()));
382
383 const QColor faintTextColor = KisPaintingTweaks::blendColors(textColor, backgroundColor, 0.4);
384 const QString &faintTextStyle = "QWidget{color: " + faintTextColor.name() + "}";
385 labelNoRecentDocs->setStyleSheet(faintTextStyle);
386 labelNoFeed->setStyleSheet(faintTextStyle);
387
388 const QColor frameColor = KisPaintingTweaks::blendColors(textColor, backgroundColor, 0.1);
389 const QString &frameQss = "{border: 1px solid " + frameColor.name() + "}";
390 recentDocsStackedWidget->setStyleSheet("QStackedWidget#recentDocsStackedWidget" + frameQss);
391 newsFrame->setStyleSheet("QFrame#newsFrame" + frameQss);
392
393 // show the dev version labels, if dev version is detected
395
396#ifdef ENABLE_UPDATERS
397 updateVersionUpdaterFrame(); // updater frame
398#endif
399
400#ifdef Q_OS_ANDROID
401 donationLink->setText(
402 QStringLiteral("<a href=\"#\">%1</a>").arg(QString(i18n("Get your Krita Supporter Badge here!"))));
403#endif
404
405#ifdef Q_OS_MACOS
406 // macOS store version should not contain external links containing donation buttons or forms
407 if (KisMacosEntitlements().sandbox()) {
408 supportKritaLink->hide();
409 supportKritaIcon->hide();
410 labelSupportText->hide();
411 kritaWebsiteLink->hide();
412 kritaWebsiteIcon->hide();
413 donationLink->hide();
414 }
415#endif
416}
417
418void KisWelcomePageWidget::dragEnterEvent(QDragEnterEvent *event)
419{
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();
425 }
426
427 return event->ignore();
428}
429
430void KisWelcomePageWidget::dropEvent(QDropEvent *event)
431{
433
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)) {
437 bool r = m_mainWindow->installBundle(url.toLocalFile());
438 if (!r) {
439 qWarning() << "Could not install bundle" << url.toLocalFile();
440 }
441 } else if (!url.isLocalFile()) {
442 QScopedPointer<QTemporaryFile> tmp(new QTemporaryFile());
443 tmp->setFileName(url.fileName());
444
445 KisRemoteFileFetcher fetcher;
446
447 if (!fetcher.fetchFile(url, tmp.data())) {
448 qWarning() << "Fetching" << url << "failed";
449 continue;
450 }
451 const auto localUrl = QUrl::fromLocalFile(tmp->fileName());
452
453 m_mainWindow->openDocument(localUrl.toLocalFile(), KisMainWindow::None);
454 } else {
455 m_mainWindow->openDocument(url.toLocalFile(), KisMainWindow::None);
456 }
457 }
458 }
459}
460
461void KisWelcomePageWidget::dragMoveEvent(QDragMoveEvent *event)
462{
464
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();
469 }
470
471 return event->ignore();
472}
473
474void KisWelcomePageWidget::dragLeaveEvent(QDragLeaveEvent */*event*/)
475{
478}
479
481{
482 if (event->type() == QEvent::FontChange) {
483 labelSupportText->setFont(largerFont());
484 donationLink->setFont(largerFont());
485 }
486}
487
488bool KisWelcomePageWidget::eventFilter(QObject *watched, QEvent *event)
489{
490 if (watched == recentDocumentsListView && event->type() == QEvent::Leave) {
491 recentDocumentsListView->clearSelection();
492 }
493 return QWidget::eventFilter(watched, event);
494}
495
496namespace {
497
498QString getAutoNewsLang()
499{
500 // Get current UI languages:
501 const QStringList uiLangs = KLocalizedString::languages();
502 QString autoNewsLang = uiLangs.first();
503 if (autoNewsLang.isEmpty()) {
504 // If nothing else, use English.
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");
516 }
517
518 return autoNewsLang;
519}
520
521} /* namespace */
522
524{
525 // Hard-coded news language data:
526 // These are languages in which the news items should be regularly
527 // translated into as of 04-09-2024.
528 // The language display names should not be translated. This reflects
529 // the language selection box on the Krita website.
530 struct Lang {
531 const QString siteCode;
532 const QString name;
533 };
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("Українська")}
557 }};
558
559 static const QString newsLangConfigName = QStringLiteral("FetchNewsLanguages");
560
561 QSharedPointer<QSet<QString>> enabledNewsLangs = QSharedPointer<QSet<QString>>::create();
562 {
563 // Initialize with the config.
564 KisConfig cfg(true);
565 auto languagesList = cfg.readList<QString>(newsLangConfigName);
566 *enabledNewsLangs = QSet(languagesList.begin(), languagesList.end());
567 }
568
569 // If no languages are selected in the config, use the automatic selection.
570 if (enabledNewsLangs->isEmpty()) {
571 enabledNewsLangs->insert(QString(getAutoNewsLang()));
572 }
573
574 for (const auto &lang : newsLangs) {
575 QAction *langItem = newsOptionsMenu->addAction(lang.name);
576 langItem->setCheckable(true);
577 // We can copy `code` into the lambda because its backing string is a
578 // static string literal.
579 const QString code = lang.siteCode;
580 connect(langItem, &QAction::toggled, newsWidget, [=](bool checked) {
581 newsWidget->toggleNewsLanguage(code, checked);
582 });
583
584 // Set the initial checked state.
585 if (enabledNewsLangs->contains(code)) {
586 langItem->setChecked(true);
587 }
588
589 // Connect this lambda after setting the initial checked state because
590 // we don't want to overwrite the config when doing the initial setup.
591 connect(langItem, &QAction::toggled, [=](bool checked) {
592 KisConfig cfg(false);
593 // It is safe to modify `enabledNewsLangs` here, because the slots
594 // are called synchronously on the UI thread so there is no need
595 // for explicit synchronization.
596 if (checked) {
597 enabledNewsLangs->insert(QString(code));
598 } else {
599 enabledNewsLangs->remove(QString(code));
600 }
601 cfg.writeList(newsLangConfigName, enabledNewsLangs->values());
602 });
603 }
604}
605
607{
608 // always flag development version
609 if (isDevelopmentBuild()) {
610 QString devBuildLabelText = QString("<a style=\"color: " +
611 blendedColor.name() +
612 " \" href=\"https://docs.krita.org/en/untranslatable_pages/triaging_bugs.html?"
613 + analyticsString + "dev-build" + "\">")
614 .append(i18n("DEV BUILD")).append("</a>");
615
616 devBuildLabel->setText(devBuildLabelText);
617 devBuildIcon->setVisible(true);
618 devBuildLabel->setVisible(true);
619 } else {
620 devBuildIcon->setVisible(false);
621 devBuildLabel->setVisible(false);
622 }
623}
624
626{
627 QString fileUrl = index.data(Qt::ToolTipRole).toString();
629}
630
632{
633 QMenu contextMenu;
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);
639 }
640 QAction *triggered = contextMenu.exec(recentDocumentsListView->mapToGlobal(pos));
641
642 if (index.isValid() && triggered == actionForget) {
643 m_mainWindow->removeRecentFile(index.data(Qt::ToolTipRole).toString());
644 }
645}
646
651
656
661
663{
664 if (!this->isVisible())
665 return;
666
667 // Don't do anything if there's no image in the clipboard
668 if (!KisClipboard::instance()->hasImage())
669 return;
670
672
674 dlg->exec();
675 dlg->deleteLater();
676}
677
679{
681 const bool modelIsEmpty = recentFilesModel->model().rowCount() == 0;
682
683 if (modelIsEmpty) {
684 recentDocsStackedWidget->setCurrentWidget(labelNoRecentDocs);
685 } else {
686 recentDocsStackedWidget->setCurrentWidget(recentDocumentsListView);
687 }
688 clearRecentFilesLink->setVisible(!modelIsEmpty);
689}
690
691#ifdef ENABLE_UPDATERS
692void KisWelcomePageWidget::slotToggleUpdateChecks(bool state)
693{
694 if (m_versionUpdater.isNull()) {
695 return;
696 }
697
698 m_networkIsAllowed = state;
699
700 if (m_networkIsAllowed) {
701 m_versionUpdater->checkForUpdate();
702 }
703
704 updateVersionUpdaterFrame();
705}
706void KisWelcomePageWidget::slotRunVersionUpdate()
707{
708 if (m_versionUpdater.isNull()) {
709 return;
710 }
711
712 if (m_networkIsAllowed) {
713 m_versionUpdater->doUpdate();
714 }
715}
716
717void KisWelcomePageWidget::slotSetUpdateStatus(KisUpdaterStatus updateStatus)
718{
719 m_updaterStatus = updateStatus;
720 updateVersionUpdaterFrame();
721}
722
723void KisWelcomePageWidget::slotShowUpdaterErrorDetails()
724{
725 QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita"), m_updaterStatus.updaterOutput());
726}
727
728void KisWelcomePageWidget::updateVersionUpdaterFrame()
729{
730 updaterFrame->setVisible(false);
731 versionNotificationLabel->setVisible(false);
732 bnVersionUpdate->setVisible(false);
733 bnErrorDetails->setVisible(false);
734
735 if (!m_networkIsAllowed || m_versionUpdater.isNull()) {
736 return;
737 }
738
739 QString versionLabelText;
740
741 if (m_updaterStatus.status() == UpdaterStatus::StatusID::UPDATE_AVAILABLE) {
742 updaterFrame->setVisible(true);
743 updaterFrame->setEnabled(true);
744 versionLabelText = i18n("New version of Krita is available.");
745 versionNotificationLabel->setVisible(true);
746 updateIcon->setIcon(KisIconUtils::loadIcon("update-medium"));
747
748 if (m_versionUpdater->hasUpdateCapability()) {
749 bnVersionUpdate->setVisible(true);
750 } else {
751 // build URL for label
752 QString downloadLink = QString(" <a style=\"color: %1; text-decoration: underline\" href=\"%2?%3\">Download Krita %4</a>")
753 .arg(blendedColor.name())
754 .arg(m_updaterStatus.downloadLink())
755 .arg(analyticsString + "version-update")
756 .arg(m_updaterStatus.availableVersion());
757
758 versionLabelText.append(downloadLink);
759 }
760
761 } else if (
762 (m_updaterStatus.status() == UpdaterStatus::StatusID::UPTODATE)
763 || (m_updaterStatus.status() == UpdaterStatus::StatusID::CHECK_ERROR)
764 || (m_updaterStatus.status() == UpdaterStatus::StatusID::IN_PROGRESS)
765 ){
766 // no notifications, if uptodate
767 // also, stay silent on check error - we do not want to generate lots of user support issues
768 // because of failing wifis and proxies over the world
769 updaterFrame->setVisible(false);
770
771 } else if (m_updaterStatus.status() == UpdaterStatus::StatusID::UPDATE_ERROR) {
772 updaterFrame->setVisible(true);
773 versionLabelText = i18n("An error occurred during the update");
774 versionNotificationLabel->setVisible(true);
775 bnErrorDetails->setVisible(true);
776 updateIcon->setIcon(KisIconUtils::loadIcon("warning"));
777 } else if (m_updaterStatus.status() == UpdaterStatus::StatusID::RESTART_REQUIRED) {
778 updaterFrame->setVisible(true);
779 versionLabelText = QString("<b>%1</b> %2").arg(i18n("Restart is required.")).arg(m_updaterStatus.details());
780 versionNotificationLabel->setVisible(true);
781 updateIcon->setIcon(KisIconUtils::loadIcon("view-refresh"));
782 }
783
784 versionNotificationLabel->setText(versionLabelText);
785 if (!blendedStyle.isNull()) {
786 versionNotificationLabel->setStyleSheet(blendedStyle);
787 }
788}
789#endif
790
791#ifdef Q_OS_ANDROID
792void KisWelcomePageWidget::initDonations()
793{
795 if (!androidDonations) {
796 qWarning("KisWelcomePage::initDonations: androidDonations is null");
797 return;
798 }
799
800 connect(donationLink, SIGNAL(linkActivated(QString)), androidDonations, SLOT(slotStartDonationFlow()));
801
802 QString bannerPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, "share/krita/donation/banner.png");
803 QPixmap pixmap(bannerPath);
804 if (pixmap.isNull()) {
805 qWarning("KisWelcomePage::initDonations: failed to load banner from '%s'", qUtf8Printable(bannerPath));
806 } else {
807 supporterBadge->setPixmap(QPixmap(bannerPath));
808 }
809
810 connect(androidDonations, SIGNAL(sigStateChanged()), this, SLOT(slotUpdateDonationState()));
811 slotUpdateDonationState();
812}
813
814void KisWelcomePageWidget::slotUpdateDonationState()
815{
816 bool linkVisible = false;
817 bool badgeVisible = false;
818
820 if (androidDonations) {
821 linkVisible = androidDonations->shouldShowDonationLink();
822 badgeVisible = androidDonations->shouldShowSupporterBadge();
823 } else {
824 qWarning("KisWelcomePageWidget::slotUpdateDonationState: android donations is null");
825 }
826
827 donationLink->setVisible(linkVisible);
828 supporterBadge->setVisible(badgeVisible);
829}
830#endif
831
833{
834 QFont larger = font();
835 // Font size may be in pixels (on Android) or points (everywhere else.)
836 qreal ratio = 1.1;
837 if (larger.pixelSize() == -1) {
838 larger.setPointSizeF(larger.pointSizeF() * ratio);
839 } else {
840 larger.setPixelSize(qRound(larger.pixelSize() * ratio));
841 }
842 return larger;
843}
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)
Definition kis_config.h:830
QList< T > readList(const QString &name, const QList< T > &defaultValue=QList< T >())
Definition kis_config.h:840
T readEntry(const QString &name, const T &defaultValue=T())
Definition kis_config.h:835
Main window for Krita.
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 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 eventFilter(QObject *watched, QEvent *event) override
void dropEvent(QDropEvent *event) override
void slotScrollerStateChanged(QScroller::State state)
void dragLeaveEvent(QDragLeaveEvent *event) override
void changeEvent(QEvent *event) override
void setMainWindow(KisMainWindow *m_mainWindow)
void slotRecentDocContextMenuRequest(const QPoint &pos)
void showDropAreaIndicator(bool show)
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)
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)
QColor blendColors(const QColor &c1, const QColor &c2, qreal r1)
KRITAVERSION_EXPORT bool isDevelopersBuild()