File indexing completed on 2024-05-12 16:59:41
0001 /* 0002 SPDX-FileCopyrightText: 2022 Fushan Wen <qydwhotmail@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "potdbackend.h" 0008 #include "config-NetworkManagerQt.h" 0009 0010 #include <QDBusConnection> 0011 #include <QFileDialog> 0012 #include <QStandardPaths> // For "Pictures" folder 0013 0014 #include <KIO/CopyJob> // For "Save Image" 0015 #include <KLocalizedString> 0016 0017 namespace 0018 { 0019 static PotdEngine *s_engine = nullptr; 0020 static int s_instanceCount = 0; 0021 } 0022 0023 PotdBackend::PotdBackend(QObject *parent) 0024 : QObject(parent) 0025 #if SUPPORT_METERED_DETECTION 0026 , m_networkManagerQtAvailable(true) 0027 #endif 0028 { 0029 if (!s_engine) { 0030 Q_ASSERT(s_instanceCount == 0); 0031 s_engine = new PotdEngine(); 0032 } 0033 s_instanceCount++; 0034 } 0035 0036 PotdBackend::~PotdBackend() 0037 { 0038 s_engine->unregisterClient(m_identifier, m_args); 0039 s_instanceCount--; 0040 0041 if (!s_instanceCount) { 0042 delete s_engine; 0043 s_engine = nullptr; 0044 } 0045 } 0046 0047 void PotdBackend::classBegin() 0048 { 0049 } 0050 0051 void PotdBackend::componentComplete() 0052 { 0053 // don't bother loading single image until all properties have settled 0054 m_ready = true; 0055 0056 // Register the identifier in the data engine 0057 registerClient(); 0058 } 0059 0060 QString PotdBackend::identifier() const 0061 { 0062 return m_identifier; 0063 } 0064 0065 void PotdBackend::setIdentifier(const QString &identifier) 0066 { 0067 if (m_identifier == identifier) { 0068 return; 0069 } 0070 0071 if (m_ready) { 0072 s_engine->unregisterClient(m_identifier, m_args); 0073 } 0074 m_identifier = identifier; 0075 registerClient(); 0076 0077 Q_EMIT identifierChanged(); 0078 } 0079 0080 QVariantList PotdBackend::arguments() const 0081 { 0082 return m_args; 0083 } 0084 0085 void PotdBackend::setArguments(const QVariantList &args) 0086 { 0087 if (m_args == args) { 0088 return; 0089 } 0090 0091 if (m_ready) { 0092 s_engine->unregisterClient(m_identifier, m_args); 0093 } 0094 m_args = args; 0095 registerClient(); 0096 0097 Q_EMIT argumentsChanged(); 0098 } 0099 0100 bool PotdBackend::loading() const 0101 { 0102 if (!m_client) { 0103 return false; 0104 } 0105 0106 return m_client->m_loading; 0107 } 0108 0109 QString PotdBackend::localUrl() const 0110 { 0111 if (!m_client) { 0112 return {}; 0113 } 0114 0115 return m_client->m_data.wallpaperLocalUrl; 0116 } 0117 0118 QUrl PotdBackend::infoUrl() const 0119 { 0120 if (!m_client) { 0121 return {}; 0122 } 0123 0124 return m_client->m_data.wallpaperInfoUrl; 0125 } 0126 0127 QUrl PotdBackend::remoteUrl() const 0128 { 0129 if (!m_client) { 0130 return {}; 0131 } 0132 0133 return m_client->m_data.wallpaperRemoteUrl; 0134 } 0135 0136 QString PotdBackend::title() const 0137 { 0138 if (!m_client) { 0139 return {}; 0140 } 0141 0142 return m_client->m_data.wallpaperTitle; 0143 } 0144 0145 QString PotdBackend::author() const 0146 { 0147 if (!m_client) { 0148 return {}; 0149 } 0150 0151 return m_client->m_data.wallpaperAuthor; 0152 } 0153 0154 int PotdBackend::doesUpdateOverMeteredConnection() const 0155 { 0156 return m_doesUpdateOverMeteredConnection; 0157 } 0158 0159 void PotdBackend::setUpdateOverMeteredConnection(int value) 0160 { 0161 value = std::clamp(value, 0, 2); 0162 const bool changed = m_doesUpdateOverMeteredConnection != value; 0163 if (changed) { 0164 m_doesUpdateOverMeteredConnection = value; 0165 Q_EMIT updateOverMeteredConnectionChanged(); 0166 } 0167 0168 if (m_ready && m_client) { 0169 #if SUPPORT_METERED_DETECTION 0170 m_client->setUpdateOverMeteredConnection(m_doesUpdateOverMeteredConnection); 0171 #else 0172 m_client->updateSource(); 0173 #endif 0174 } 0175 } 0176 0177 void PotdBackend::saveImage() 0178 { 0179 if (m_client->m_data.wallpaperLocalUrl.isEmpty()) { 0180 return; 0181 } 0182 0183 auto sanitizeFileName = [](const QString &name) { 0184 if (name.isEmpty()) { 0185 return name; 0186 } 0187 0188 const char notAllowedChars[] = ",^@={}[]~!?:&*\"|#%<>$\"'();`'/\\"; 0189 QString sanitizedName(name); 0190 0191 for (const char *c = notAllowedChars; *c; c++) { 0192 sanitizedName.replace(QLatin1Char(*c), QLatin1Char('-')); 0193 } 0194 0195 return sanitizedName; 0196 }; 0197 0198 const QStringList &locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); 0199 const QString path = locations.isEmpty() ? QStandardPaths::standardLocations(QStandardPaths::HomeLocation).at(0) : locations.at(0); 0200 0201 QString defaultFileName = m_client->m_metadata.name().trimmed(); 0202 0203 if (!m_client->m_data.wallpaperTitle.isEmpty()) { 0204 defaultFileName += QLatin1Char('-') + m_client->m_data.wallpaperTitle.trimmed(); 0205 if (!m_client->m_data.wallpaperAuthor.isEmpty()) { 0206 defaultFileName += QLatin1Char('-') + m_client->m_data.wallpaperAuthor.trimmed(); 0207 } 0208 } else { 0209 // Use current date 0210 if (!defaultFileName.isEmpty()) { 0211 defaultFileName += QLatin1Char('-'); 0212 } 0213 defaultFileName += QDate::currentDate().toString(); 0214 } 0215 0216 m_savedUrl = QUrl::fromLocalFile( // 0217 QFileDialog::getSaveFileName( // 0218 nullptr, // 0219 i18ndc("plasma_wallpaper_org.kde.potd", "@title:window", "Save Today's Picture"), // 0220 path + "/" + sanitizeFileName(defaultFileName) + ".jpg", // 0221 i18ndc("plasma_wallpaper_org.kde.potd", "@label:listbox Template for file dialog", "JPEG image (*.jpeg *.jpg *.jpe)"), // 0222 nullptr, // 0223 QFileDialog::DontConfirmOverwrite // KIO::CopyJob will show the confirmation dialog. 0224 ) // 0225 ); 0226 0227 if (m_savedUrl.isEmpty() || !m_savedUrl.isValid()) { 0228 return; 0229 } 0230 0231 m_savedFolder = QUrl::fromLocalFile(QFileInfo(m_savedUrl.toLocalFile()).absolutePath()); 0232 0233 KIO::CopyJob *copyJob = KIO::copy(QUrl::fromLocalFile(m_client->m_data.wallpaperLocalUrl), m_savedUrl, KIO::HideProgressInfo); 0234 connect(copyJob, &KJob::finished, this, [this](KJob *job) { 0235 if (job->error()) { 0236 m_saveStatusMessage = job->errorText(); 0237 if (m_saveStatusMessage.isEmpty()) { 0238 m_saveStatusMessage = i18ndc("plasma_wallpaper_org.kde.potd", "@info:status after a save action", "The image was not saved."); 0239 } 0240 m_saveStatus = FileOperationStatus::Failed; 0241 Q_EMIT saveStatusChanged(); 0242 } else { 0243 m_saveStatusMessage = i18ndc("plasma_wallpaper_org.kde.potd", 0244 "@info:status after a save action %1 file path %2 basename", 0245 "The image was saved as <a href=\"%1\">%2</a>", 0246 m_savedUrl.toString(), 0247 m_savedUrl.fileName()); 0248 m_saveStatus = FileOperationStatus::Successful; 0249 Q_EMIT saveStatusChanged(); 0250 } 0251 }); 0252 copyJob->start(); 0253 } 0254 0255 void PotdBackend::registerClient() 0256 { 0257 if (!m_ready) { 0258 return; 0259 } 0260 0261 m_client = s_engine->registerClient(m_identifier, m_args); 0262 0263 if (!m_client) { 0264 // Invalid identifier 0265 return; 0266 } 0267 0268 connect(m_client, &PotdClient::imageChanged, this, &PotdBackend::imageChanged); 0269 connect(m_client, &PotdClient::loadingChanged, this, &PotdBackend::loadingChanged); 0270 connect(m_client, &PotdClient::localUrlChanged, this, &PotdBackend::localUrlChanged); 0271 connect(m_client, &PotdClient::infoUrlChanged, this, &PotdBackend::infoUrlChanged); 0272 connect(m_client, &PotdClient::remoteUrlChanged, this, &PotdBackend::remoteUrlChanged); 0273 connect(m_client, &PotdClient::titleChanged, this, &PotdBackend::titleChanged); 0274 connect(m_client, &PotdClient::authorChanged, this, &PotdBackend::authorChanged); 0275 0276 // Refresh the desktop wallpaper and the information in config dialog 0277 Q_EMIT loadingChanged(); 0278 Q_EMIT localUrlChanged(); 0279 Q_EMIT infoUrlChanged(); 0280 Q_EMIT remoteUrlChanged(); 0281 Q_EMIT titleChanged(); 0282 Q_EMIT authorChanged(); 0283 0284 // For updateSource() 0285 setUpdateOverMeteredConnection(m_doesUpdateOverMeteredConnection); 0286 }