File indexing completed on 2024-05-12 04:21:20
0001 0002 // REFACT0R: Remote open/save file logic is duplicated in kpDocument. 0003 // HITODO: Test when remote file support in KDE 4 stabilizes 0004 0005 /* This file is part of the KDE libraries 0006 Copyright (C) 1999 Waldo Bastian (bastian@kde.org) 0007 Copyright (C) 2007 Clarence Dang (dang@kde.org) 0008 0009 This library is free software; you can redistribute it and/or 0010 modify it under the terms of the GNU Library General Public 0011 License as published by the Free Software Foundation; either 0012 version 2 of the License. 0013 0014 This library is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 Library General Public License for more details. 0018 0019 You should have received a copy of the GNU Library General Public License 0020 along with this library; see the file COPYING.LIB. If not, write to 0021 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0022 Boston, MA 02110-1301, USA. 0023 */ 0024 //----------------------------------------------------------------------------- 0025 // KDE color collection 0026 0027 #define DEBUG_KP_COLOR_COLLECTION 0 0028 0029 #include "kpColorCollection.h" 0030 0031 #include "kpUrlFormatter.h" 0032 0033 #include <KJobWidgets> 0034 #include <KIO/StoredTransferJob> 0035 #include <KIO/FileCopyJob> 0036 #include <KLocalizedString> 0037 #include <KMessageBox> 0038 #include "kpLogCategories.h" 0039 0040 #include <QDir> 0041 #include <QFile> 0042 #include <QSaveFile> 0043 #include <QStandardPaths> 0044 #include <QTemporaryFile> 0045 #include <QTextStream> 0046 #include <QUrl> 0047 0048 struct ColorNode 0049 { 0050 ColorNode(const QColor &c, const QString &n) 0051 : color(c), name(n) {} 0052 0053 QColor color; 0054 QString name; 0055 }; 0056 0057 //--------------------------------------------------------------------- 0058 0059 Q_LOGGING_CATEGORY(kpLogColorCollection, "kp.colorCollection") 0060 0061 //BEGIN kpColorCollectionPrivate 0062 class kpColorCollectionPrivate 0063 { 0064 public: 0065 kpColorCollectionPrivate(); 0066 kpColorCollectionPrivate(const kpColorCollectionPrivate&); 0067 0068 QList<ColorNode> colorList; 0069 QString name; 0070 QString desc; 0071 kpColorCollection::Editable editable; 0072 }; 0073 0074 kpColorCollectionPrivate::kpColorCollectionPrivate() 0075 : editable(kpColorCollection::Yes) 0076 { 0077 } 0078 0079 kpColorCollectionPrivate::kpColorCollectionPrivate(const kpColorCollectionPrivate& p) 0080 : colorList(p.colorList), name(p.name), desc(p.desc), editable(p.editable) 0081 { 0082 } 0083 //END kpColorCollectionPrivate 0084 0085 //--------------------------------------------------------------------- 0086 0087 QStringList 0088 kpColorCollection::installedCollections() 0089 { 0090 QStringList paletteList; 0091 0092 QStringList paths = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("colors"), 0093 QStandardPaths::LocateDirectory); 0094 for (const auto &path : paths) { 0095 paletteList.append(QDir(path).entryList(QStringList(), QDir::Files)); 0096 } 0097 0098 return paletteList; 0099 } 0100 0101 kpColorCollection::kpColorCollection() 0102 { 0103 d = new kpColorCollectionPrivate(); 0104 } 0105 0106 kpColorCollection::kpColorCollection(const kpColorCollection &p) 0107 { 0108 d = new kpColorCollectionPrivate(*p.d); 0109 } 0110 0111 kpColorCollection::~kpColorCollection() 0112 { 0113 // Need auto-save? 0114 delete d; 0115 } 0116 0117 static void CouldNotOpenDialog (const QUrl &url, QWidget *parent) 0118 { 0119 KMessageBox::error (parent, 0120 i18n ("Could not open color palette \"%1\".", 0121 kpUrlFormatter::PrettyFilename (url))); 0122 } 0123 0124 // TODO: Set d->editable? 0125 bool 0126 kpColorCollection::open(const QUrl &url, QWidget *parent) 0127 { 0128 if (url.isEmpty()) { 0129 return false; 0130 } 0131 0132 KIO::StoredTransferJob *job = KIO::storedGet (url); 0133 KJobWidgets::setWindow (job, parent); 0134 0135 if (!job->exec ()) 0136 { 0137 #if DEBUG_KP_COLOR_COLLECTION 0138 qCDebug(kpLogColorCollection) << "\tcould not download"; 0139 #endif 0140 ::CouldNotOpenDialog (url, parent); 0141 return false; 0142 } 0143 0144 const QByteArray &data = job->data(); 0145 QTextStream stream(data); 0146 0147 // Read first line 0148 // Expected "GIMP Palette" or "KDE RGB Palette" or "KDE RGBA Palette" 0149 QString line = stream.readLine(); 0150 if (line.indexOf(QLatin1String(" Palette")) == -1) 0151 { 0152 KMessageBox::error (parent, 0153 i18n ("Could not open color palette \"%1\" - unsupported format.\n" 0154 "The file may be corrupt.", 0155 kpUrlFormatter::PrettyFilename (url))); 0156 return false; 0157 } 0158 0159 bool hasAlpha = line == QLatin1String("KDE RGBA Palette"); // new format includes alpha 0160 0161 QList <ColorNode> newColorList; 0162 QString newDesc; 0163 0164 while( !stream.atEnd() ) 0165 { 0166 line = stream.readLine(); 0167 if ( !line.isEmpty() && (line[0] == QLatin1Char('#')) ) 0168 { 0169 // This is a comment line 0170 line = line.mid(1); // Strip '#' 0171 line = line.trimmed(); // Strip remaining white space.. 0172 if (!line.isEmpty()) 0173 { 0174 newDesc += line+QLatin1Char('\n'); // Add comment to description 0175 } 0176 } 0177 else 0178 { 0179 // This is a color line, hopefully 0180 line = line.trimmed(); 0181 if (line.isEmpty()) continue; 0182 int r, g, b, a = 255; 0183 int pos = 0; 0184 bool ok = false; 0185 0186 if ( hasAlpha ) 0187 ok = (sscanf(line.toLatin1().constData(), "%d %d %d %d%n", &r, &g, &b, &a, &pos) >= 4); 0188 else 0189 ok = (sscanf(line.toLatin1().constData(), "%d %d %d%n", &r, &g, &b, &pos) >= 3); 0190 0191 if ( ok ) 0192 { 0193 r = qBound(0, r, 255); 0194 g = qBound(0, g, 255); 0195 b = qBound(0, b, 255); 0196 a = qBound(0, a, 255); 0197 QString name = line.mid(pos).trimmed(); 0198 newColorList.append(ColorNode(QColor(r, g, b, a), name)); 0199 } 0200 } 0201 } 0202 0203 d->colorList = newColorList; 0204 d->name.clear (); 0205 d->desc = newDesc; 0206 0207 return true; 0208 } 0209 0210 static void CouldNotOpenKDEDialog (const QString &name, QWidget *parent) 0211 { 0212 KMessageBox::error (parent, 0213 i18n ("Could not open KDE color palette \"%1\".", name)); 0214 } 0215 0216 bool 0217 kpColorCollection::openKDE(const QString &name, QWidget *parent) 0218 { 0219 #if DEBUG_KP_COLOR_COLLECTION 0220 qCDebug(kpLogColorCollection) << "name=" << name; 0221 #endif 0222 0223 if (name.isEmpty()) 0224 { 0225 #if DEBUG_KP_COLOR_COLLECTION 0226 qCDebug(kpLogColorCollection) << "name.isEmpty"; 0227 #endif 0228 ::CouldNotOpenKDEDialog (name, parent); 0229 return false; 0230 } 0231 0232 QString filename = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, 0233 QStringLiteral("colors/") + name); 0234 if (filename.isEmpty()) 0235 { 0236 #if DEBUG_KP_COLOR_COLLECTION 0237 qCDebug(kpLogColorCollection) << "could not find file"; 0238 #endif 0239 ::CouldNotOpenKDEDialog (name, parent); 0240 return false; 0241 } 0242 0243 // (this will pop up an error dialog on failure) 0244 if (!open (QUrl::fromLocalFile (filename), parent)) 0245 { 0246 #if DEBUG_KP_COLOR_COLLECTION 0247 qCDebug(kpLogColorCollection) << "could not open"; 0248 #endif 0249 return false; 0250 } 0251 0252 d->name = name; 0253 #if DEBUG_KP_COLOR_COLLECTION 0254 qCDebug(kpLogColorCollection) << "opened"; 0255 #endif 0256 return true; 0257 } 0258 0259 static void CouldNotSaveDialog (const QUrl &url, QWidget *parent) 0260 { 0261 // TODO: use file.errorString() 0262 KMessageBox::error (parent, 0263 i18n ("Could not save color palette as \"%1\".", 0264 kpUrlFormatter::PrettyFilename (url))); 0265 } 0266 0267 static void SaveToFile (kpColorCollectionPrivate *d, QIODevice *device) 0268 { 0269 // HITODO: QTextStream can fail but does not report errors. 0270 // Bug in KColorCollection too. 0271 QTextStream str (device); 0272 0273 QString description = d->desc.trimmed(); 0274 description = QLatin1Char('#') + description.split(QLatin1Char('\n'), Qt::KeepEmptyParts).join(QLatin1String("\n#")); 0275 0276 str << "KDE RGBA Palette\n"; 0277 str << description << "\n"; 0278 for (const auto &node : d->colorList) 0279 { 0280 // Added for KolourPaint. 0281 if ( !node.color.isValid() ) 0282 continue; 0283 0284 int r, g, b, a; 0285 node.color.getRgb(&r, &g, &b, &a); 0286 str << r << " " << g << " " << b << " " << a << " " << node.name << "\n"; 0287 } 0288 0289 str.flush(); 0290 } 0291 0292 bool 0293 kpColorCollection::saveAs(const QUrl &url, QWidget *parent) const 0294 { 0295 if (url.isLocalFile ()) 0296 { 0297 const QString filename = url.toLocalFile (); 0298 0299 // sync: All failure exit paths _must_ call QSaveFile::cancelWriting() or 0300 // else, the QSaveFile destructor will overwrite the file, 0301 // <filename>, despite the failure. 0302 QSaveFile atomicFileWriter (filename); 0303 { 0304 if (!atomicFileWriter.open (QIODevice::WriteOnly)) 0305 { 0306 // We probably don't need this as <filename> has not been 0307 // opened. 0308 atomicFileWriter.cancelWriting (); 0309 0310 #if DEBUG_KP_COLOR_COLLECTION 0311 qCDebug(kpLogColorCollection) << "\treturning false because could not open QSaveFile" 0312 << " error=" << atomicFileWriter.error (); 0313 #endif 0314 ::CouldNotSaveDialog (url, parent); 0315 return false; 0316 } 0317 0318 // Write to local temporary file. 0319 ::SaveToFile (d, &atomicFileWriter); 0320 0321 // Atomically overwrite local file with the temporary file 0322 // we saved to. 0323 if (!atomicFileWriter.commit ()) 0324 { 0325 atomicFileWriter.cancelWriting (); 0326 0327 #if DEBUG_KP_COLOR_COLLECTION 0328 qCDebug(kpLogColorCollection) << "\tcould not close QSaveFile"; 0329 #endif 0330 ::CouldNotSaveDialog (url, parent); 0331 return false; 0332 } 0333 } // sync QSaveFile.cancelWriting() 0334 } 0335 // Remote file? 0336 else 0337 { 0338 // Create temporary file that is deleted when the variable goes 0339 // out of scope. 0340 QTemporaryFile tempFile; 0341 if (!tempFile.open ()) 0342 { 0343 #if DEBUG_KP_COLOR_COLLECTION 0344 qCDebug(kpLogColorCollection) << "\treturning false because could not open tempFile"; 0345 #endif 0346 ::CouldNotSaveDialog (url, parent); 0347 return false; 0348 } 0349 0350 // Write to local temporary file. 0351 ::SaveToFile (d, &tempFile); 0352 0353 // Collect name of temporary file now, as QTemporaryFile::fileName() 0354 // stops working after close() is called. 0355 const QString tempFileName = tempFile.fileName (); 0356 #if DEBUG_KP_COLOR_COLLECTION 0357 qCDebug(kpLogColorCollection) << "\ttempFileName='" << tempFileName << "'"; 0358 #endif 0359 Q_ASSERT (!tempFileName.isEmpty ()); 0360 0361 tempFile.close (); 0362 if (tempFile.error () != QFile::NoError) 0363 { 0364 #if DEBUG_KP_COLOR_COLLECTION 0365 qCDebug(kpLogColorCollection) << "\treturning false because could not close"; 0366 #endif 0367 ::CouldNotSaveDialog (url, parent); 0368 return false; 0369 } 0370 0371 // Copy local temporary file to overwrite remote. 0372 KIO::FileCopyJob *job = KIO::file_copy (QUrl::fromLocalFile (tempFileName), 0373 url, 0374 -1, 0375 KIO::Overwrite); 0376 KJobWidgets::setWindow (job, parent); 0377 if (!job->exec ()) 0378 { 0379 #if DEBUG_KP_COLOR_COLLECTION 0380 qCDebug(kpLogColorCollection) << "\treturning false because could not upload"; 0381 #endif 0382 ::CouldNotSaveDialog (url, parent); 0383 return false; 0384 } 0385 } 0386 0387 d->name.clear (); 0388 return true; 0389 } 0390 0391 QString kpColorCollection::description() const 0392 { 0393 return d->desc; 0394 } 0395 0396 void kpColorCollection::setDescription(const QString &desc) 0397 { 0398 d->desc = desc; 0399 } 0400 0401 QString kpColorCollection::name() const 0402 { 0403 return d->name; 0404 } 0405 0406 void kpColorCollection::setName(const QString &name) 0407 { 0408 d->name = name; 0409 } 0410 0411 kpColorCollection::Editable kpColorCollection::editable() const 0412 { 0413 return d->editable; 0414 } 0415 0416 void kpColorCollection::setEditable(Editable editable) 0417 { 0418 d->editable = editable; 0419 } 0420 0421 int kpColorCollection::count() const 0422 { 0423 return (int) d->colorList.count(); 0424 } 0425 0426 void kpColorCollection::resize(int newCount) 0427 { 0428 if (newCount == count()) 0429 return; 0430 else if (newCount < count()) 0431 { 0432 d->colorList.erase(d->colorList.begin() + newCount, d->colorList.end()); 0433 } 0434 else if (newCount > count()) 0435 { 0436 while(newCount > count()) 0437 { 0438 const int ret = addColor(QColor(), QString()/*color name*/); 0439 Q_ASSERT(ret == count() - 1); 0440 } 0441 } 0442 } 0443 0444 kpColorCollection& 0445 kpColorCollection::operator=( const kpColorCollection &p) 0446 { 0447 if (&p == this) return *this; 0448 d->colorList = p.d->colorList; 0449 d->name = p.d->name; 0450 d->desc = p.d->desc; 0451 d->editable = p.d->editable; 0452 return *this; 0453 } 0454 0455 QColor 0456 kpColorCollection::color(int index) const 0457 { 0458 if ((index < 0) || (index >= count())) 0459 return {}; 0460 0461 return d->colorList[index].color; 0462 } 0463 0464 int 0465 kpColorCollection::findColor(const QColor &color) const 0466 { 0467 for (int i = 0; i < d->colorList.size(); ++i) 0468 { 0469 if (d->colorList[i].color == color) 0470 return i; 0471 } 0472 return -1; 0473 } 0474 0475 QString 0476 kpColorCollection::name(int index) const 0477 { 0478 if ((index < 0) || (index >= count())) 0479 return {}; 0480 0481 return d->colorList[index].name; 0482 } 0483 0484 QString kpColorCollection::name(const QColor &color) const 0485 { 0486 return name(findColor(color)); 0487 } 0488 0489 int 0490 kpColorCollection::addColor(const QColor &newColor, const QString &newColorName) 0491 { 0492 d->colorList.append(ColorNode(newColor, newColorName)); 0493 return count() - 1; 0494 } 0495 0496 int 0497 kpColorCollection::changeColor(int index, 0498 const QColor &newColor, 0499 const QString &newColorName) 0500 { 0501 if ((index < 0) || (index >= count())) 0502 return -1; 0503 0504 ColorNode& node = d->colorList[index]; 0505 node.color = newColor; 0506 node.name = newColorName; 0507 0508 return index; 0509 } 0510 0511 int kpColorCollection::changeColor(const QColor &oldColor, 0512 const QColor &newColor, 0513 const QString &newColorName) 0514 { 0515 return changeColor( findColor(oldColor), newColor, newColorName); 0516 } 0517