diff --git a/README.md b/README.md index 0c9274dadc..75748e91b7 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,52 @@ ![Picture](https://krita.org/wp-content/uploads/2019/04/krita-logo-2019.png) | Jenkins CI Name | Master | Stable | | --------------- | ------ | ------ | | OpenSuse Qt 5.12 | [![Build Status](https://build.kde.org/job/Extragear/job/krita/job/kf5-qt5%20SUSEQt5.12/badge/icon)](https://build.kde.org/job/Extragear/job/krita/job/kf5-qt5%20SUSEQt5.12/) |[![Build Status](https://build.kde.org/buildStatus/icon?job=Extragear%2Fkrita%2Fstable-kf5-qt5+SUSEQt5.12)](https://build.kde.org/job/Extragear/job/krita/job/stable-kf5-qt5%20SUSEQt5.12/)| | FreeBSD Qt 5.13 | [![Build Status](https://build.kde.org/job/Extragear/job/krita/job/kf5-qt5%20FreeBSDQt5.13/badge/icon)](https://build.kde.org/job/Extragear/job/krita/job/kf5-qt5%20FreeBSDQt5.13/) |[![Build Status](https://build.kde.org/job/Extragear/job/krita/job/stable-kf5-qt5%20FreeBSDQt5.13/badge/icon)](https://build.kde.org/job/Extragear/job/krita/job/stable-kf5-qt5%20FreeBSDQt5.13/)| Krita is a free and open source digital painting application. It is for artists who want to create professional work from start to end. Krita is used by comic book artists, illustrators, concept artists, matte and texture painters and in the digital VFX industry. If you are reading this on Github, be aware that this is just a mirror. Our real -code repository is provided by KDE: https://invent.kde.org/kde/krita +code repository is provided by KDE: https://invent.kde.org/graphics/krita.git ![Picture](https://krita.org/wp-content/uploads/2016/04/krita-30-screenshot.jpg) ### User Manual https://docs.krita.org/en/user_manual.html ### Development Notes and Build Instructions If you're building on Windows or OSX you'll need to build some third-party dependencies first. You should look at the README in the 3rdparty folder for directions. If you're building on Linux, please follow [the online documentation](https://docs.krita.org/en/untranslatable_pages/building_krita.html). Other developer guides, notes and wiki: https://docs.krita.org/en/untranslatable_pages.html Apidox: https://api.kde.org/extragear-api/graphics-apidocs/krita/html/index.html ### Bugs and Wishes https://bugs.kde.org/buglist.cgi?bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=ASSIGNED&bug_status=REOPENED&list_id=1315444&product=krita&query_format=advanced ### Discussion Forum * https://krita-artists.org/ * http://forum.kde.org/viewforum.php?f=136 ### IRC channel Most of the developers hang out here. If you are interested in helping with the project this is a great place to start. Many of the developers based in Europe so they may be offline depending on when you join. irc.freenode.net, #krita ### Project Website https://www.krita.org ### License Krita as a whole is licensed under the GNU Public License, Version 3. Individual files may have a different, but compatible license. diff --git a/libs/ui/KisWelcomePageWidget.cpp b/libs/ui/KisWelcomePageWidget.cpp index b1b4297429..df5929cf48 100644 --- a/libs/ui/KisWelcomePageWidget.cpp +++ b/libs/ui/KisWelcomePageWidget.cpp @@ -1,586 +1,586 @@ /* This file is part of the KDE project * Copyright (C) 2018 Scott Petrovic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisWelcomePageWidget.h" #include #include #include #include #include #include #include #include "kis_action_manager.h" #include "kactioncollection.h" #include "kis_action.h" #include "KConfigGroup" #include "KSharedConfig" #include #include #include "kis_icon_utils.h" #include "krita_utils.h" #include "KoStore.h" #include "kis_config.h" #include "KisDocument.h" #include #include #include #include #include #include #include #include #include "config-updaters.h" #ifdef ENABLE_UPDATERS #ifdef Q_OS_LINUX #include #endif #include #endif #include #include #include KisWelcomePageWidget::KisWelcomePageWidget(QWidget *parent) : QWidget(parent) { setupUi(this); recentDocumentsListView->setDragEnabled(false); recentDocumentsListView->viewport()->setAutoFillBackground(false); recentDocumentsListView->setSpacing(2); // set up URLs that go to web browser manualLink->setTextFormat(Qt::RichText); manualLink->setTextInteractionFlags(Qt::TextBrowserInteraction); manualLink->setOpenExternalLinks(true); gettingStartedLink->setTextFormat(Qt::RichText); gettingStartedLink->setTextInteractionFlags(Qt::TextBrowserInteraction); gettingStartedLink->setOpenExternalLinks(true); supportKritaLink->setTextFormat(Qt::RichText); supportKritaLink->setTextInteractionFlags(Qt::TextBrowserInteraction); supportKritaLink->setOpenExternalLinks(true); userCommunityLink->setTextFormat(Qt::RichText); userCommunityLink->setTextInteractionFlags(Qt::TextBrowserInteraction); userCommunityLink->setOpenExternalLinks(true); kritaWebsiteLink->setTextFormat(Qt::RichText); kritaWebsiteLink->setTextInteractionFlags(Qt::TextBrowserInteraction); kritaWebsiteLink->setOpenExternalLinks(true); sourceCodeLink->setTextFormat(Qt::RichText); sourceCodeLink->setTextInteractionFlags(Qt::TextBrowserInteraction); sourceCodeLink->setOpenExternalLinks(true); poweredByKDELink->setTextFormat(Qt::RichText); poweredByKDELink->setTextInteractionFlags(Qt::TextBrowserInteraction); poweredByKDELink->setOpenExternalLinks(true); kdeIcon->setIconSize(QSize(20, 20)); kdeIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("kde")).pixmap(20)); versionNotificationLabel->setTextFormat(Qt::RichText); versionNotificationLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); versionNotificationLabel->setOpenExternalLinks(true); devBuildIcon->setIcon(KisIconUtils::loadIcon("warning")); devBuildLabel->setTextFormat(Qt::RichText); devBuildLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); devBuildLabel->setOpenExternalLinks(true); devBuildLabel->setVisible(false); updaterFrame->setVisible(false); versionNotificationLabel->setVisible(false); bnVersionUpdate->setVisible(false); bnErrorDetails->setVisible(false); connect(chkShowNews, SIGNAL(toggled(bool)), newsWidget, SLOT(toggleNews(bool))); #ifdef ENABLE_UPDATERS connect(chkShowNews, SIGNAL(toggled(bool)), this, SLOT(slotToggleUpdateChecks(bool))); #endif #ifdef Q_OS_ANDROID // checking this widgets crashes the app, so it is better for it to be hidden for now newsWidget->hide(); helpTitleLabel_2->hide(); chkShowNews->hide(); #endif // configure the News area KisConfig cfg(true); m_checkUpdates = cfg.readEntry("FetchNews", false); #ifdef ENABLE_UPDATERS #ifndef Q_OS_ANDROID // Setup version updater, but do not check for them, unless the user explicitely // wants to check for updates. // * No updater is created for Linux/Steam, Windows/Steam and Windows/Store distributions, // as those stores have their own updating mechanism. // * STEAMAPPID(Windows)/SteamAppId(Linux) environment variable is set when Krita is run from Steam. // The environment variables are not public API. // * AppxManifest.xml file in the installation directory indicates MS Store version #if defined Q_OS_LINUX if (!qEnvironmentVariableIsSet("SteamAppId")) { // do not create updater for linux/steam if (qEnvironmentVariableIsSet("APPIMAGE")) { m_versionUpdater.reset(new KisAppimageUpdater()); } else { m_versionUpdater.reset(new KisManualUpdater()); } } #elif defined Q_OS_WIN QString appxManifestFilePath = QString("%1/../AppxManifest.xml").arg(QCoreApplication::applicationDirPath()); QFileInfo appxManifestFileInfo(appxManifestFilePath); if (!appxManifestFileInfo.exists() && !qEnvironmentVariableIsSet("STEAMAPPID")) { m_versionUpdater.reset(new KisManualUpdater()); KisUsageLogger::log("Non-store package - creating updater"); } else { KisUsageLogger::log("detected appx or steam package - not creating the updater"); } #else // always create updater for MacOS m_versionUpdater.reset(new KisManualUpdater()); #endif // Q_OS_* if (!m_versionUpdater.isNull()) { connect(bnVersionUpdate, SIGNAL(clicked()), this, SLOT(slotRunVersionUpdate())); connect(bnErrorDetails, SIGNAL(clicked()), this, SLOT(slotShowUpdaterErrorDetails())); connect(m_versionUpdater.data(), SIGNAL(sigUpdateCheckStateChange(KisUpdaterStatus)), this, SLOT(slotSetUpdateStatus(const KisUpdaterStatus&))); if (m_checkUpdates) { // only if the user wants them m_versionUpdater->checkForUpdate(); } } #endif // ifndef Q_OS_ANDROID #endif // ENABLE_UPDATERS chkShowNews->setChecked(m_checkUpdates); setAcceptDrops(true); } KisWelcomePageWidget::~KisWelcomePageWidget() { } void KisWelcomePageWidget::setMainWindow(KisMainWindow* mainWin) { if (mainWin) { m_mainWindow = mainWin; // set the shortcut links from actions (only if a shortcut exists) if ( mainWin->viewManager()->actionManager()->actionByName("file_new")->shortcut().toString() != "") { newFileLinkShortcut->setText(QString("(") + mainWin->viewManager()->actionManager()->actionByName("file_new")->shortcut().toString() + QString(")")); } if (mainWin->viewManager()->actionManager()->actionByName("file_open")->shortcut().toString() != "") { openFileShortcut->setText(QString("(") + mainWin->viewManager()->actionManager()->actionByName("file_open")->shortcut().toString() + QString(")")); } connect(recentDocumentsListView, SIGNAL(clicked(QModelIndex)), this, SLOT(recentDocumentClicked(QModelIndex))); // we need the view manager to actually call actions, so don't create the connections // until after the view manager is set connect(newFileLink, SIGNAL(clicked(bool)), this, SLOT(slotNewFileClicked())); connect(openFileLink, SIGNAL(clicked(bool)), this, SLOT(slotOpenFileClicked())); connect(clearRecentFilesLink, SIGNAL(clicked(bool)), mainWin, SLOT(clearRecentFiles())); slotUpdateThemeColors(); // allows RSS news items to apply analytics tracking. newsWidget->setAnalyticsTracking("?" + analyticsString); } } void KisWelcomePageWidget::showDropAreaIndicator(bool show) { if (!show) { QString dropFrameStyle = "QFrame#dropAreaIndicator { border: 0px }"; dropFrameBorder->setStyleSheet(dropFrameStyle); } else { QColor textColor = qApp->palette().color(QPalette::Text); QColor backgroundColor = qApp->palette().color(QPalette::Background); QColor blendedColor = KritaUtils::blendColors(textColor, backgroundColor, 0.8); // QColor.name() turns it into a hex/web format QString dropFrameStyle = QString("QFrame#dropAreaIndicator { border: 2px dotted ").append(blendedColor.name()).append(" }") ; dropFrameBorder->setStyleSheet(dropFrameStyle); } } void KisWelcomePageWidget::slotUpdateThemeColors() { textColor = qApp->palette().color(QPalette::Text); backgroundColor = qApp->palette().color(QPalette::Background); // make the welcome screen labels a subtle color so it doesn't clash with the main UI elements blendedColor = KritaUtils::blendColors(textColor, backgroundColor, 0.8); blendedStyle = QString("color: ").append(blendedColor.name()); // what labels to change the color... startTitleLabel->setStyleSheet(blendedStyle); recentDocumentsLabel->setStyleSheet(blendedStyle); helpTitleLabel->setStyleSheet(blendedStyle); newFileLinkShortcut->setStyleSheet(blendedStyle); openFileShortcut->setStyleSheet(blendedStyle); clearRecentFilesLink->setStyleSheet(blendedStyle); recentDocumentsListView->setStyleSheet(blendedStyle); newFileLink->setStyleSheet(blendedStyle); openFileLink->setStyleSheet(blendedStyle); // giving the drag area messaging a dotted border QString dottedBorderStyle = QString("border: 2px dotted ").append(blendedColor.name()).append("; color:").append(blendedColor.name()).append( ";"); dragImageHereLabel->setStyleSheet(dottedBorderStyle); // make drop area QFrame have a dotted line dropFrameBorder->setObjectName("dropAreaIndicator"); QString dropFrameStyle = QString("QFrame#dropAreaIndicator { border: 4px dotted ").append(blendedColor.name()).append("}"); dropFrameBorder->setStyleSheet(dropFrameStyle); // only show drop area when we have a document over the empty area showDropAreaIndicator(false); // add icons for new and open settings to make them stand out a bit more openFileLink->setIconSize(QSize(30, 30)); newFileLink->setIconSize(QSize(30, 30)); openFileLink->setIcon(KisIconUtils::loadIcon("document-open")); newFileLink->setIcon(KisIconUtils::loadIcon("document-new")); kdeIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("kde")).pixmap(20)); // HTML links seem to be a bit more stubborn with theme changes... setting inline styles to help with color change userCommunityLink->setText(QString("") .append(i18n("User Community")).append("")); gettingStartedLink->setText(QString("") .append(i18n("Getting Started")).append("")); manualLink->setText(QString("") .append(i18n("User Manual")).append("")); supportKritaLink->setText(QString("") .append(i18n("Support Krita")).append("")); kritaWebsiteLink->setText(QString("") .append(i18n("Krita Website")).append("")); - sourceCodeLink->setText(QString("") + sourceCodeLink->setText(QString("") .append(i18n("Source Code")).append("")); poweredByKDELink->setText(QString("") .append(i18n("Powered by KDE")).append("")); // show the dev version labels, if dev version is detected showDevVersionHighlight(); #ifdef ENABLE_UPDATERS updateVersionUpdaterFrame(); // updater frame #endif // re-populate recent files since they might have themed icons populateRecentDocuments(); } void KisWelcomePageWidget::populateRecentDocuments() { m_recentFilesModel.clear(); // clear existing data before it gets re-populated // grab recent files data int numRecentFiles = m_mainWindow->recentFilesUrls().length() > 5 ? 5 : m_mainWindow->recentFilesUrls().length(); // grab at most 5 KisFileIconCreator iconCreator; for (int i = 0; i < numRecentFiles; i++ ) { QStandardItem *recentItem = new QStandardItem(1,2); // 1 row, 1 column recentItem->setIcon(KisIconUtils::loadIcon("document-export")); QString recentFileUrlPath = m_mainWindow->recentFilesUrls().at(i).toLocalFile(); QString fileName = QFileInfo(recentFileUrlPath).fileName(); QList brokenUrls; if (m_thumbnailMap.contains(recentFileUrlPath)) { recentItem->setIcon(m_thumbnailMap[recentFileUrlPath]); } else { QIcon icon; bool success = iconCreator.createFileIcon(recentFileUrlPath, icon, devicePixelRatioF()); if (success) { recentItem->setIcon(icon); m_thumbnailMap[recentFileUrlPath] = recentItem->icon(); } else { brokenUrls << m_mainWindow->recentFilesUrls().at(i); } } Q_FOREACH(const QUrl &url, brokenUrls) { m_mainWindow->removeRecentUrl(url); } // set the recent object with the data if (brokenUrls.isEmpty() || brokenUrls.last().toLocalFile() != recentFileUrlPath) { recentItem->setText(fileName); // what to display for the item recentItem->setToolTip(recentFileUrlPath); m_recentFilesModel.appendRow(recentItem); } } // hide clear and Recent files title if there are none bool hasRecentFiles = m_mainWindow->recentFilesUrls().length() > 0; recentDocumentsLabel->setVisible(hasRecentFiles); clearRecentFilesLink->setVisible(hasRecentFiles); recentDocumentsListView->setIconSize(QSize(48, 48)); recentDocumentsListView->setModel(&m_recentFilesModel); } void KisWelcomePageWidget::dragEnterEvent(QDragEnterEvent *event) { //qDebug() << "dragEnterEvent formats" << event->mimeData()->formats() << "urls" << event->mimeData()->urls() << "has images" << event->mimeData()->hasImage(); showDropAreaIndicator(true); if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisWelcomePageWidget::dropEvent(QDropEvent *event) { //qDebug() << "KisWelcomePageWidget::dropEvent() formats" << event->mimeData()->formats() << "urls" << event->mimeData()->urls() << "has images" << event->mimeData()->hasImage(); showDropAreaIndicator(false); if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { if (url.toLocalFile().endsWith(".bundle")) { bool r = m_mainWindow->installBundle(url.toLocalFile()); if (!r) { qWarning() << "Could not install bundle" << url.toLocalFile(); } } else { m_mainWindow->openDocument(url, KisMainWindow::None); } } } } void KisWelcomePageWidget::dragMoveEvent(QDragMoveEvent *event) { //qDebug() << "dragMoveEvent"; m_mainWindow->dragMoveEvent(event); if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisWelcomePageWidget::dragLeaveEvent(QDragLeaveEvent */*event*/) { //qDebug() << "dragLeaveEvent"; showDropAreaIndicator(false); m_mainWindow->dragLeave(); } void KisWelcomePageWidget::showDevVersionHighlight() { // always flag developement version if (isDevelopmentBuild()) { QString devBuildLabelText = QString("") .append(i18n("DEV BUILD")).append(""); devBuildLabel->setText(devBuildLabelText); devBuildIcon->setVisible(true); devBuildLabel->setVisible(true); } else { devBuildIcon->setVisible(false); devBuildLabel->setVisible(false); } } void KisWelcomePageWidget::recentDocumentClicked(QModelIndex index) { QString fileUrl = index.data(Qt::ToolTipRole).toString(); m_mainWindow->openDocument(QUrl::fromLocalFile(fileUrl), KisMainWindow::None ); } bool KisWelcomePageWidget::isDevelopmentBuild() { QString versionString = KritaVersionWrapper::versionString(true); if (versionString.contains("git")) { return true; } else { return false; } } void KisWelcomePageWidget::slotNewFileClicked() { m_mainWindow->slotFileNew(); } void KisWelcomePageWidget::slotOpenFileClicked() { m_mainWindow->slotFileOpen(); } #ifdef ENABLE_UPDATERS void KisWelcomePageWidget::slotToggleUpdateChecks(bool state) { if (m_versionUpdater.isNull()) { return; } m_checkUpdates = state; if (m_checkUpdates) { m_versionUpdater->checkForUpdate(); } updateVersionUpdaterFrame(); } void KisWelcomePageWidget::slotRunVersionUpdate() { if (m_versionUpdater.isNull()) { return; } if (m_checkUpdates) { m_versionUpdater->doUpdate(); } } void KisWelcomePageWidget::slotSetUpdateStatus(KisUpdaterStatus updateStatus) { m_updaterStatus = updateStatus; updateVersionUpdaterFrame(); } void KisWelcomePageWidget::slotShowUpdaterErrorDetails() { QMessageBox::warning(0, i18nc("@title:window", "Krita"), m_updaterStatus.updaterOutput()); } void KisWelcomePageWidget::updateVersionUpdaterFrame() { updaterFrame->setVisible(false); versionNotificationLabel->setVisible(false); bnVersionUpdate->setVisible(false); bnErrorDetails->setVisible(false); if (!m_checkUpdates || m_versionUpdater.isNull()) { return; } QString versionLabelText; if (m_updaterStatus.status() == UpdaterStatus::StatusID::UPDATE_AVAILABLE) { updaterFrame->setVisible(true); updaterFrame->setEnabled(true); versionLabelText = i18n("New version of Krita is available."); versionNotificationLabel->setVisible(true); updateIcon->setIcon(KisIconUtils::loadIcon("update-medium")); if (m_versionUpdater->hasUpdateCapability()) { bnVersionUpdate->setVisible(true); // bnVersionUpdate->setEnabled(true); } else { // build URL for label QString downloadLink = QString(" Download Krita %4") .arg(blendedColor.name()) .arg(m_updaterStatus.downloadLink()) .arg(analyticsString + "version-update") .arg(m_updaterStatus.availableVersion()); versionLabelText.append(downloadLink); } } else if ( (m_updaterStatus.status() == UpdaterStatus::StatusID::UPTODATE) || (m_updaterStatus.status() == UpdaterStatus::StatusID::CHECK_ERROR) || (m_updaterStatus.status() == UpdaterStatus::StatusID::IN_PROGRESS) ){ // no notifications, if uptodate // also, stay silent on check error - we do not want to generate lots of user support issues // because of failing wifis and proxies over the world updaterFrame->setVisible(false); } else if (m_updaterStatus.status() == UpdaterStatus::StatusID::UPDATE_ERROR) { updaterFrame->setVisible(true); versionLabelText = i18n("An error occurred during the update"); versionNotificationLabel->setVisible(true); bnErrorDetails->setVisible(true); updateIcon->setIcon(KisIconUtils::loadIcon("warning")); // bnErrorDetails->setEnabled(true); } else if (m_updaterStatus.status() == UpdaterStatus::StatusID::RESTART_REQUIRED) { updaterFrame->setVisible(true); versionLabelText = QString("%1 %2").arg(i18n("Restart is required.")).arg(m_updaterStatus.details()); versionNotificationLabel->setVisible(true); updateIcon->setIcon(KisIconUtils::loadIcon("view-refresh")); } versionNotificationLabel->setText(versionLabelText); if (!blendedStyle.isNull()) { versionNotificationLabel->setStyleSheet(blendedStyle); } } #endif