File indexing completed on 2024-05-12 16:39:36
0001 /* This file is part of the KDE project 0002 Copyright (C) 2005-2011 Jarosław Staniek <staniek@kde.org> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "kexiblobbuffer.h" 0021 #include <kexiutils/utils.h> 0022 0023 #include <assert.h> 0024 0025 #include <QFile> 0026 #include <QFileInfo> 0027 #include <QBuffer> 0028 #include <QPixmap> 0029 #include <QHash> 0030 #include <QMimeDatabase> 0031 #include <QMimeType> 0032 #include <QDebug> 0033 #include <QImageReader> 0034 0035 #include <KDbConnection> 0036 #include <KDbQuerySchema> 0037 #include <KDbRecordData> 0038 0039 Q_GLOBAL_STATIC(KexiBLOBBuffer, _buffer) 0040 0041 //----------------- 0042 0043 class Q_DECL_HIDDEN KexiBLOBBuffer::Private 0044 { 0045 public: 0046 Private() 0047 : maxId(0) 0048 , conn(0) 0049 { 0050 } 0051 ~Private() { 0052 foreach(Item* item, inMemoryItems) { 0053 delete item; 0054 } 0055 inMemoryItems.clear(); 0056 foreach(Item* item, storedItems) { 0057 delete item; 0058 } 0059 storedItems.clear(); 0060 } 0061 Id_t maxId; //!< Used to compute maximal recently used identifier for unstored BLOB 0062 //! @todo will be changed to QHash<quint64, Item> 0063 QHash<Id_t, Item*> inMemoryItems; //!< for unstored BLOBs 0064 QHash<Id_t, Item*> storedItems; //!< for stored items 0065 QHash<QString, Item*> itemsByURL; 0066 //! @todo KEXI3 use equivalent of QPointer<KDbConnection> 0067 KDbConnection *conn; 0068 }; 0069 0070 //----------------- 0071 0072 KexiBLOBBuffer::Handle::Handle(Item* item) 0073 : m_item(item) 0074 { 0075 if (m_item) 0076 m_item->refs++; 0077 } 0078 0079 KexiBLOBBuffer::Handle::Handle(const Handle& handle) 0080 { 0081 *this = handle; 0082 } 0083 0084 KexiBLOBBuffer::Handle::Handle() 0085 : m_item(0) 0086 { 0087 } 0088 0089 KexiBLOBBuffer::Handle::~Handle() 0090 { 0091 if (m_item) { 0092 m_item->refs--; 0093 if (m_item->refs <= 0) 0094 KexiBLOBBuffer::self()->removeItem(m_item->id, m_item->stored); 0095 } 0096 } 0097 0098 KexiBLOBBuffer::Handle& KexiBLOBBuffer::Handle::operator=(const Handle & handle) 0099 { 0100 m_item = handle.m_item; 0101 if (m_item) 0102 m_item->refs++; 0103 return *this; 0104 } 0105 0106 void KexiBLOBBuffer::Handle::setStoredWidthID(KexiBLOBBuffer::Id_t id) 0107 { 0108 if (!m_item) 0109 return; 0110 if (m_item->stored) { 0111 qWarning() << "object for id=" << id << " is aleady stored"; 0112 return; 0113 } 0114 0115 KexiBLOBBuffer::self()->takeItem(m_item); 0116 m_item->id = id; //new id 0117 m_item->stored = true; 0118 //! @todo What about other handles for this item? 0119 //! @todo They were assuming it's unstored item, but it's stored now.... 0120 KexiBLOBBuffer::self()->insertItem(m_item); 0121 } 0122 0123 //----------------- 0124 0125 KexiBLOBBuffer::Item::Item(const QByteArray& data, KexiBLOBBuffer::Id_t ident, bool _stored, 0126 const QString& _name, const QString& _caption, const QString& _mimeType, 0127 Id_t _folderId, const QPixmap& pixmap) 0128 : name(_name), caption(_caption), mimeType(_mimeType), refs(0), 0129 id(ident), folderId(_folderId), stored(_stored), 0130 m_pixmapLoaded(new bool(false)/*workaround for pixmap() const*/) 0131 { 0132 if (pixmap.isNull()) 0133 m_pixmap = new QPixmap(); 0134 else 0135 m_pixmap = new QPixmap(pixmap); 0136 0137 if (data.isEmpty()) 0138 m_data = new QByteArray(); 0139 else 0140 m_data = new QByteArray(data); 0141 } 0142 0143 KexiBLOBBuffer::Item::~Item() 0144 { 0145 delete m_pixmap; 0146 m_pixmap = 0; 0147 delete m_data; 0148 m_data = 0; 0149 delete m_pixmapLoaded; 0150 } 0151 0152 QPixmap KexiBLOBBuffer::Item::pixmap() const 0153 { 0154 //! @todo ... 0155 if (!*m_pixmapLoaded && m_pixmap->isNull() && !m_data->isEmpty()) { 0156 const QMimeDatabase db; 0157 const QMimeType mime(db.mimeTypeForName(mimeType)); 0158 if (!mime.isValid() 0159 || !QImageReader::supportedMimeTypes().contains(mimeType.toLatin1()) 0160 || !KexiUtils::loadPixmapFromData(m_pixmap, *m_data, mime.preferredSuffix().toLatin1())) 0161 { 0162 //! @todo inform about error? 0163 } 0164 *m_pixmapLoaded = true; 0165 } 0166 return *m_pixmap; 0167 } 0168 0169 /*! @return Extension for QPixmap::save() from @a mimeType. 0170 @todo default PNG ok? */ 0171 static QString formatFromMimeType(const QString& mimeType, const QString& defaultFormat = "PNG") 0172 { 0173 QMimeDatabase db; 0174 const QMimeType mime = db.mimeTypeForName(mimeType); 0175 if (!mime.isValid()) { 0176 return defaultFormat; 0177 } 0178 return mime.preferredSuffix(); 0179 } 0180 0181 QByteArray KexiBLOBBuffer::Item::data() const 0182 { 0183 if (!m_data->isEmpty()) 0184 return *m_data; 0185 0186 if (m_data->isEmpty() && m_pixmap->isNull()) 0187 return QByteArray(); 0188 0189 if (m_data->isEmpty() && !m_pixmap->isNull()) { 0190 //convert pixmap to byte array 0191 //(do it only on demand) 0192 QBuffer buffer(m_data); 0193 if (!buffer.open(QIODevice::WriteOnly)) { 0194 //! @todo err msg 0195 qWarning() << "!QBuffer::open()"; 0196 } 0197 if (!m_pixmap->save(&buffer, formatFromMimeType(mimeType).toLatin1())) 0198 { 0199 //! @todo err msg 0200 qWarning() << "!QPixmap::save()"; 0201 } 0202 } 0203 return *m_data; 0204 } 0205 0206 //----------------- 0207 0208 KexiBLOBBuffer::KexiBLOBBuffer() 0209 : QObject() 0210 , d(new Private()) 0211 { 0212 } 0213 0214 KexiBLOBBuffer::~KexiBLOBBuffer() 0215 { 0216 delete d; 0217 } 0218 0219 KexiBLOBBuffer::Handle KexiBLOBBuffer::insertPixmap(const QUrl &url) 0220 { 0221 if (url.isEmpty()) 0222 return KexiBLOBBuffer::Handle(); 0223 if (!url.isValid()) { 0224 qWarning() << "INVALID URL" << url; 0225 return KexiBLOBBuffer::Handle(); 0226 } 0227 //! @todo what about searching by filename only and then compare data? 0228 Item * item = d->itemsByURL.value(url.toDisplayString()); 0229 if (item) 0230 return KexiBLOBBuffer::Handle(item); 0231 0232 QString fileName = url.isLocalFile() ? url.toLocalFile() : url.toDisplayString(); 0233 //! @todo download the file if remote, then set fileName properly 0234 QFile f(fileName); 0235 if (!f.open(QIODevice::ReadOnly)) { 0236 //! @todo err msg 0237 return KexiBLOBBuffer::Handle(); 0238 } 0239 QByteArray data(f.readAll()); 0240 if (f.error() != QFile::NoError) { 0241 //! @todo err msg 0242 return KexiBLOBBuffer::Handle(); 0243 } 0244 QFileInfo fi(url.fileName()); 0245 QString caption(fi.baseName().replace('_', ' ').simplified()); 0246 QMimeDatabase db; 0247 const QMimeType mimeType(db.mimeTypeForFileNameAndData(fileName, data)); 0248 0249 item = new Item(data, ++d->maxId, /*!stored*/false, url.fileName(), caption, mimeType.name()); 0250 insertItem(item); 0251 0252 //cache 0253 item->prettyURL = url.toDisplayString(); 0254 d->itemsByURL.insert(url.toDisplayString(), item); 0255 return KexiBLOBBuffer::Handle(item); 0256 } 0257 0258 KexiBLOBBuffer::Handle KexiBLOBBuffer::insertObject(const QByteArray& data, 0259 const QString& name, const QString& caption, const QString& mimeType, KexiBLOBBuffer::Id_t identifier) 0260 { 0261 KexiBLOBBuffer::Id_t newIdentifier; 0262 if (identifier > 0) 0263 newIdentifier = identifier; 0264 else 0265 newIdentifier = ++d->maxId; 0266 0267 Item *item = new Item(data, newIdentifier, identifier > 0, name, caption, mimeType); 0268 insertItem(item); 0269 return KexiBLOBBuffer::Handle(item); 0270 } 0271 0272 KexiBLOBBuffer::Handle KexiBLOBBuffer::insertPixmap(const QPixmap& pixmap) 0273 { 0274 if (pixmap.isNull()) 0275 return KexiBLOBBuffer::Handle(); 0276 0277 Item * item = new Item( 0278 QByteArray(), //(pixmap will be converted to byte array on demand) 0279 ++d->maxId, 0280 false, //not stored 0281 QString(), 0282 QString(), 0283 "image/png", //!< @todo OK? What about jpegs? 0284 0, //folder id 0285 pixmap); 0286 0287 insertItem(item); 0288 return KexiBLOBBuffer::Handle(item); 0289 } 0290 0291 KexiBLOBBuffer::Handle KexiBLOBBuffer::objectForId(Id_t id, bool stored) 0292 { 0293 if (id <= 0) 0294 return KexiBLOBBuffer::Handle(); 0295 if (stored) { 0296 Item *item = d->storedItems.value(id); 0297 if (item || !d->conn) 0298 return KexiBLOBBuffer::Handle(item); 0299 //retrieve stored BLOB: 0300 Q_ASSERT(d->conn); 0301 KDbTableSchema *blobsTable = d->conn->tableSchema("kexi__blobs"); 0302 if (!blobsTable) { 0303 //! @todo err msg 0304 return KexiBLOBBuffer::Handle(); 0305 } 0306 /* QStringList where; 0307 where << "o_id"; 0308 KDbPreparedStatement::Ptr st = d->conn->prepareStatement( 0309 KDbPreparedStatement::SelectStatement, *blobsTable, where);*/ 0310 //! @todo use KDbPreparedStatement 0311 KDbQuerySchema schema; 0312 schema.addField(blobsTable->field("o_data")); 0313 schema.addField(blobsTable->field("o_name")); 0314 schema.addField(blobsTable->field("o_caption")); 0315 schema.addField(blobsTable->field("o_mime")); 0316 schema.addField(blobsTable->field("o_folder_id")); 0317 QString errorMessage; 0318 QString errorDescription; 0319 if (!schema.addToWhereExpression(blobsTable->field("o_id"), QVariant((qint64)id), 0320 '=', &errorMessage, &errorDescription)) 0321 { 0322 qWarning() << "message=" << errorMessage 0323 << "description=" << errorDescription; 0324 return KexiBLOBBuffer::Handle(); 0325 } 0326 KDbRecordData recordData; 0327 tristate res = d->conn->querySingleRecord(&schema, &recordData); 0328 if (res != true || recordData.size() < 4) { 0329 //! @todo err msg 0330 qWarning() << "id=" << id << "stored=" << stored 0331 << ": res!=true || recordData.size()<4; res==" << res.toString() 0332 << "recordData.size()==" << recordData.size(); 0333 return KexiBLOBBuffer::Handle(); 0334 } 0335 0336 item = new Item( 0337 recordData.at(0).toByteArray(), 0338 id, 0339 true, //stored 0340 recordData.at(1).toString(), 0341 recordData.at(2).toString(), 0342 recordData.at(3).toString(), 0343 (Id_t)recordData.at(4).toInt() //!< @todo folder id: fix Id_t for Qt4 0344 ); 0345 0346 insertItem(item); 0347 return KexiBLOBBuffer::Handle(item); 0348 } 0349 return KexiBLOBBuffer::Handle(d->inMemoryItems.value(id)); 0350 } 0351 0352 KexiBLOBBuffer::Handle KexiBLOBBuffer::objectForId(Id_t id) 0353 { 0354 KexiBLOBBuffer::Handle h(objectForId(id, false/* !stored */)); 0355 if (h) 0356 return h; 0357 return objectForId(id, true/*stored*/); 0358 } 0359 0360 void KexiBLOBBuffer::removeItem(Id_t id, bool stored) 0361 { 0362 Item *item; 0363 if (stored) 0364 item = d->storedItems.take(id); 0365 else 0366 item = d->inMemoryItems.take(id); 0367 0368 if (item && !item->prettyURL.isEmpty()) { 0369 d->itemsByURL.remove(item->prettyURL); 0370 } 0371 delete item; 0372 } 0373 0374 void KexiBLOBBuffer::takeItem(Item *item) 0375 { 0376 Q_ASSERT(item); 0377 if (item->stored) 0378 d->storedItems.take(item->id); 0379 else 0380 d->inMemoryItems.take(item->id); 0381 } 0382 0383 void KexiBLOBBuffer::insertItem(Item *item) 0384 { 0385 Q_ASSERT(item); 0386 if (item->stored) 0387 d->storedItems.insert(item->id, item); 0388 else 0389 d->inMemoryItems.insert(item->id, item); 0390 } 0391 0392 void KexiBLOBBuffer::setConnection(KDbConnection *conn) 0393 { 0394 KexiBLOBBuffer::self()->d->conn = conn; 0395 } 0396 0397 KexiBLOBBuffer* KexiBLOBBuffer::self() 0398 { 0399 return _buffer; 0400 } 0401