You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kexi/core/kexiblobbuffer.cpp

374 lines
9.4 KiB

/* This file is part of the KDE project
Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
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 "kexiblobbuffer.h"
#include <assert.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqbuffer.h>
#include <kdebug.h>
#include <kstaticdeleter.h>
#include <kimageio.h>
#include <kexidb/connection.h>
static KStaticDeleter<KexiBLOBBuffer> m_bufferDeleter;
static KexiBLOBBuffer* m_buffer = 0;
//-----------------
class KexiBLOBBuffer::Private
{
public:
Private()
: maxId(0)
, inMemoryItems(1009)
, storedItems(1009)
, itemsByURL(1009)
{
}
Id_t maxId; //!< Used to compute maximal recently used identifier for unstored BLOB
//! @todo will be changed to TQHash<quint64, Item>
TQIntDict<Item> inMemoryItems; //!< for unstored BLOBs
TQIntDict<Item> storedItems; //!< for stored items
TQDict<Item> itemsByURL;
TQGuardedPtr<KexiDB::Connection> conn;
};
//-----------------
KexiBLOBBuffer::Handle::Handle(Item* item)
: m_item(item)
{
if (m_item)
m_item->refs++;
}
KexiBLOBBuffer::Handle::Handle(const Handle& handle)
{
*this = handle;
}
KexiBLOBBuffer::Handle::Handle()
: m_item(0)
{
}
KexiBLOBBuffer::Handle::~Handle()
{
if (m_item) {
m_item->refs--;
if (m_item->refs<=0)
KexiBLOBBuffer::self()->removeItem(m_item->id, m_item->stored);
}
}
KexiBLOBBuffer::Handle& KexiBLOBBuffer::Handle::operator=(const Handle& handle)
{
m_item = handle.m_item;
if (m_item)
m_item->refs++;
return *this;
}
void KexiBLOBBuffer::Handle::setStoredWidthID(KexiBLOBBuffer::Id_t id)
{
if (!m_item)
return;
if (m_item->stored) {
kdWarning() << "KexiBLOBBuffer::Handle::setStoredWidthID(): object for id=" << id
<< " is aleady stored" << endl;
return;
}
KexiBLOBBuffer::self()->takeItem(m_item);
m_item->id = id; //new id
m_item->stored = true;
//! @todo What about other handles for this item?
//! @todo They were assuming it's unstored item, but it's stored now....
KexiBLOBBuffer::self()->insertItem(m_item);
}
//-----------------
KexiBLOBBuffer::Item::Item(const TQByteArray& data, KexiBLOBBuffer::Id_t ident, bool _stored,
const TQString& _name, const TQString& _caption, const TQString& _mimeType,
Id_t _folderId, const TQPixmap& pixmap)
: name(_name), caption(_caption), mimeType(_mimeType), refs(0),
id(ident), folderId(_folderId), stored(_stored),
m_pixmapLoaded(new bool(false)/*workaround for pixmap() const*/)
{
if (pixmap.isNull())
m_pixmap = new TQPixmap();
else
m_pixmap = new TQPixmap(pixmap);
if (data.isEmpty())
m_data = new TQByteArray();
else
m_data = new TQByteArray(data);
}
KexiBLOBBuffer::Item::~Item()
{
kexipluginsdbg << "KexiBLOBBuffer::Item::~Item()" << endl;
delete m_pixmap;
m_pixmap = 0;
delete m_data;
m_data = 0;
delete m_pixmapLoaded;
}
TQPixmap KexiBLOBBuffer::Item::pixmap() const
{
if (!*m_pixmapLoaded && m_pixmap->isNull() && !m_data->isEmpty()) {
TQString type( KImageIO::typeForMime(mimeType) );
if (!KImageIO::canRead( type ) || !m_pixmap->loadFromData(*m_data, type.latin1())) {
//! @todo inform about error?
}
*m_pixmapLoaded = true;
}
return *m_pixmap;
}
TQByteArray KexiBLOBBuffer::Item::data() const
{
if (!m_data->isEmpty())
return *m_data;
if (m_data->isEmpty() && m_pixmap->isNull())
return TQByteArray();
if (m_data->isEmpty() && !m_pixmap->isNull()) {
//convert pixmap to byte array
//(do it only on demand)
TQBuffer buffer( *m_data );
buffer.open( IO_WriteOnly );
m_pixmap->save( &buffer, mimeType.isEmpty() ? (const char*)"PNG"/*! @todo default? */ : mimeType.latin1() );
}
return *m_data;
}
//-----------------
KexiBLOBBuffer::KexiBLOBBuffer()
: TQObject()
, d(new Private())
{
Q_ASSERT(!m_buffer);
d->inMemoryItems.setAutoDelete(true);
d->storedItems.setAutoDelete(true);
}
KexiBLOBBuffer::~KexiBLOBBuffer()
{
delete d;
}
KexiBLOBBuffer::Handle KexiBLOBBuffer::insertPixmap(const KURL& url)
{
if (url.isEmpty() )
return KexiBLOBBuffer::Handle();
if (!url.isValid()) {
kexipluginswarn << "::insertPixmap: INVALID URL '" << url << "'" << endl;
return KexiBLOBBuffer::Handle();
}
//! @todo what about searching by filename only and then compare data?
Item * item = d->itemsByURL.tqfind(url.prettyURL());
if (item)
return KexiBLOBBuffer::Handle(item);
TQString fileName = url.isLocalFile() ? url.path() : url.prettyURL();
//! @todo download the file if remote, then set fileName properly
TQFile f(fileName);
if (!f.open(IO_ReadOnly)) {
//! @todo err msg
return KexiBLOBBuffer::Handle();
}
TQString mimeType( KImageIO::mimeType( fileName ) );
TQByteArray data( f.readAll() );
if (f.status()!=IO_Ok) {
//! @todo err msg
return KexiBLOBBuffer::Handle();
}
TQFileInfo fi(url.fileName());
TQString caption(fi.baseName().tqreplace('_', " ").simplifyWhiteSpace());
item = new Item(data, ++d->maxId, /*!stored*/false, url.fileName(), caption, mimeType);
insertItem(item);
//cache
item->prettyURL = url.prettyURL();
d->itemsByURL.tqreplace(url.prettyURL(), item);
return KexiBLOBBuffer::Handle(item);
}
KexiBLOBBuffer::Handle KexiBLOBBuffer::insertObject(const TQByteArray& data,
const TQString& name, const TQString& caption, const TQString& mimeType, KexiBLOBBuffer::Id_t identifier)
{
KexiBLOBBuffer::Id_t newIdentifier;
if (identifier>0)
newIdentifier = identifier;
else
newIdentifier = ++d->maxId;
Item *item = new Item(data, newIdentifier, identifier>0, name, caption, mimeType);
insertItem( item );
return KexiBLOBBuffer::Handle(item);
}
KexiBLOBBuffer::Handle KexiBLOBBuffer::insertPixmap(const TQPixmap& pixmap)
{
if (pixmap.isNull())
return KexiBLOBBuffer::Handle();
Item * item = new Item(
TQByteArray(), //(pixmap will be converted to byte array on demand)
++d->maxId,
false, //not stored
TQString(),
TQString(),
"image/png", //!< @todo OK? What about jpegs?
0, //folder id
pixmap);
insertItem(item);
return KexiBLOBBuffer::Handle(item);
}
KexiBLOBBuffer::Handle KexiBLOBBuffer::objectForId(Id_t id, bool stored)
{
if (id<=0)
return KexiBLOBBuffer::Handle();
if (stored) {
Item *item = d->storedItems.tqfind(id);
if (item || !d->conn)
return KexiBLOBBuffer::Handle(item);
//retrieve stored BLOB:
//#if 0
assert(d->conn);
KexiDB::TableSchema *blobsTable = d->conn->tableSchema("kexi__blobs");
if (!blobsTable) {
//! @todo err msg
return KexiBLOBBuffer::Handle();
}
/* TQStringList where;
where << "o_id";
KexiDB::PreparedStatement::Ptr st = d->conn->prepareStatement(
KexiDB::PreparedStatement::SelectStatement, *blobsTable, where);*/
//! @todo use PreparedStatement
KexiDB::QuerySchema schema;
schema.addField( blobsTable->field("o_data") );
schema.addField( blobsTable->field("o_name") );
schema.addField( blobsTable->field("o_caption") );
schema.addField( blobsTable->field("o_mime") );
schema.addField( blobsTable->field("o_folder_id") );
schema.addToWhereExpression(blobsTable->field("o_id"), TQVariant((TQ_LLONG)id));
KexiDB::RowData rowData;
tristate res = d->conn->querySingleRecord(
schema,
// TQString::tqfromLatin1("SELECT o_data, o_name, o_caption, o_mime FROM kexi__blobs where o_id=")
// +TQString::number(id),
rowData);
if (res!=true || rowData.size()<4) {
//! @todo err msg
kdWarning() << "KexiBLOBBuffer::objectForId("<<id<<","<<stored
<<"): res!=true || rowData.size()<4; res=="<<res.toString()<<" rowData.size()=="<<rowData.size()<< endl;
return KexiBLOBBuffer::Handle();
}
item = new Item(
rowData[0].toByteArray(),
id,
true, //stored
rowData[1].toString(),
rowData[2].toString(),
rowData[3].toString(),
(Id_t)rowData[4].toInt() //!< @todo folder id: fix Id_t for TQt4
);
insertItem(item);
return KexiBLOBBuffer::Handle(item);
//#endif
}
else
return KexiBLOBBuffer::Handle(d->inMemoryItems.tqfind(id));
}
KexiBLOBBuffer::Handle KexiBLOBBuffer::objectForId(Id_t id)
{
KexiBLOBBuffer::Handle h(objectForId(id, false/*!stored*/));
if (h)
return h;
return objectForId(id, true/*stored*/);
}
void KexiBLOBBuffer::removeItem(Id_t id, bool stored)
{
Item *item;
if (stored)
item = d->storedItems.take(id);
else
item = d->inMemoryItems.take(id);
if (item && !item->prettyURL.isEmpty()) {
d->itemsByURL.remove(item->prettyURL);
}
delete item;
}
void KexiBLOBBuffer::takeItem(Item *item)
{
assert(item);
if (item->stored)
d->storedItems.take(item->id);
else
d->inMemoryItems.take(item->id);
}
void KexiBLOBBuffer::insertItem(Item *item)
{
assert(item);
if (item->stored)
d->storedItems.insert(item->id, item);
else
d->inMemoryItems.insert(item->id, item);
}
void KexiBLOBBuffer::setConnection(KexiDB::Connection *conn)
{
KexiBLOBBuffer::self()->d->conn = conn;
}
KexiBLOBBuffer* KexiBLOBBuffer::self()
{
if(!m_buffer) {
m_bufferDeleter.setObject( m_buffer, new KexiBLOBBuffer() );
}
return m_buffer;
}
#include "kexiblobbuffer.moc"