Created
March 29, 2015 23:54
-
-
Save danieloneill/aaed4045bddeb88774a0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "callbackmodel.h" | |
#include <time.h> | |
#include <QDebug> | |
CallbackModel::CallbackModel(QObject *parent): | |
QAbstractListModel(parent), | |
m_rowCount(0), | |
m_cache(true), | |
m_cacheSize(0), | |
m_requestDelay(20) | |
{ | |
m_requestTimer.setSingleShot(true); | |
connect( &m_requestTimer, SIGNAL(timeout()), this, SLOT(slotRequestData()) ); | |
} | |
CallbackModel::~CallbackModel() | |
{ | |
} | |
void CallbackModel::setRecord( qulonglong idx, QVariant record ) | |
{ | |
m_records[ idx ] = record; | |
QModelIndex tl = index( idx, 0 ); | |
QModelIndex br = index( idx, 0 ); | |
m_recordsNeeded.removeOne( idx ); | |
emit dataChanged( tl, br ); | |
} | |
void CallbackModel::setRecords( qulonglong first, QVariant records ) | |
{ | |
if( records.isNull() || !records.isValid() ) | |
return; | |
QVariantList rl = records.toList(); | |
if( rl.isEmpty() ) | |
return; | |
int y; | |
for( y=0; y < rl.count(); y++ ) | |
{ | |
m_records[ y+first ] = rl[ y ]; | |
m_recordsNeeded.removeOne( y+first ); | |
} | |
QModelIndex tl = index( first, 0 ); | |
QModelIndex br = index( first + rl.count() - 1, 0 ); | |
emit dataChanged( tl, br ); | |
} | |
void CallbackModel::setRows(qulonglong count) | |
{ | |
qulonglong oldcount = m_rowCount; | |
const QModelIndex idx; | |
if( count == oldcount ) | |
return; | |
if( count > oldcount ) | |
{ | |
beginInsertRows( idx, oldcount, count-1); | |
m_rowCount = count; | |
endInsertRows(); | |
} | |
else | |
{ | |
beginRemoveRows( idx, count, oldcount); | |
m_rowCount = count; | |
endRemoveRows(); | |
// If we have data past the index, remove it. | |
for( qulonglong x=count; x < oldcount; x++ ) | |
m_records.remove( x ); | |
} | |
emit rowCountChanged(); | |
QModelIndex tl = index( oldcount, 0 ); | |
QModelIndex br = index( count, 0 ); | |
emit dataChanged( tl, br ); | |
} | |
int CallbackModel::rowCount(const QModelIndex & parent) const | |
{ | |
if( parent.isValid() ) | |
return 0; | |
return m_rowCount; | |
} | |
void CallbackModel::requestData( qulonglong row ) | |
{ | |
m_mutex.lock(); | |
if( m_recordsNeeded.contains(row) ) | |
{ | |
m_mutex.unlock(); | |
return; | |
} | |
//qDebug() << "requestData(" << row << ")"; | |
m_recordsNeeded.append( row ); | |
m_requestTimer.start(m_requestDelay); | |
m_mutex.unlock(); | |
} | |
QVariant CallbackModel::data(const QModelIndex & index, int role) const | |
{ | |
Q_UNUSED(role) | |
QVariantMap invalid; | |
invalid["valid"] = false; | |
if( !m_records.contains( index.row() ) ) | |
{ | |
((CallbackModel *)this)->requestData( index.row() ); | |
//qWarning() << "CallbackModel::data for: " << index.row(); | |
return invalid; | |
} | |
QVariant r = m_records[ index.row() ]; | |
return r; | |
/* | |
if( index.column() >= r.count() ) | |
{ | |
((CallbackModel *)this)->requestData( index.row() ); | |
return invalid; | |
} | |
return r[ index.column() ]; | |
*/ | |
} | |
bool CallbackModel::hasIndex(int row, int column, const QModelIndex & parent) const | |
{ | |
//qDebug() << "hasIndex(" << row << ", " << column << ")"; | |
if( parent.isValid() ) | |
return false; | |
if( row > m_rowCount ) | |
return false; | |
if( row < 0 ) | |
return false; | |
if( column > 1 ) | |
return false; | |
return true; | |
} | |
void CallbackModel::slotRequestData() | |
{ | |
//qDebug() << "slotRequestData()!"; | |
if( m_recordsNeeded.length() == 0 ) | |
return; | |
qSort(m_recordsNeeded); | |
qulonglong lowest=m_recordsNeeded.first(), highest=m_recordsNeeded.last(); | |
qDebug() << "Requesting records between " << lowest << " and " << highest; | |
emit recordsRequested( lowest, highest ); | |
} | |
/* | |
bool CallbackModel::canFetchMore(const QModelIndex & parent) const | |
{ | |
if( parent ) | |
return; | |
if( m_rowCount > m_cacheSize ) | |
return true; | |
return false; | |
} | |
void CallbackModel::fetchMore(const QModelIndex & parent) | |
{ | |
} | |
*/ | |
QHash<int, QByteArray> CallbackModel::roleNames() const | |
{ | |
QHash<int, QByteArray> roles; | |
roles[Qt::UserRole] = "entry"; | |
return roles; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CALLBACKMODEL_H | |
#define CALLBACKMODEL_H | |
#include <QAbstractListModel> | |
#include <QHash> | |
#include <QList> | |
#include <QMutex> | |
#include <QQuickItem> | |
#include <QTimer> | |
#include <QVariant> | |
class CallbackModel : public QAbstractListModel | |
{ | |
Q_OBJECT | |
Q_DISABLE_COPY(CallbackModel) | |
// Properties: | |
qulonglong m_rowCount; | |
bool m_cache; | |
qulonglong m_cacheSize; | |
int m_requestDelay; | |
QMutex m_mutex; | |
QHash< qulonglong, QVariant > m_records; | |
QList< qulonglong > m_recordsNeeded; | |
QTimer m_requestTimer; | |
public: | |
CallbackModel(QObject *parent = 0); | |
~CallbackModel(); | |
Q_PROPERTY( int requestDelay MEMBER m_requestDelay NOTIFY requestDelayChanged ) | |
Q_PROPERTY( qulonglong rows READ rows WRITE setRows NOTIFY rowCountChanged ) | |
Q_PROPERTY( bool cache MEMBER m_cache NOTIFY cacheChanged ) | |
Q_PROPERTY( bool cacheSize MEMBER m_cacheSize NOTIFY cacheSizeChanged ) | |
Q_INVOKABLE static CallbackModel *create() { return new CallbackModel(); } | |
Q_INVOKABLE qulonglong rows() { return m_rowCount; } | |
Q_INVOKABLE void setRows(qulonglong count); | |
Q_INVOKABLE void setRecord( qulonglong index, QVariant record ); // 'record' should be convertable to QVariantList | |
Q_INVOKABLE void setRecords( qulonglong first, QVariant records ); // 'records' should be convertable to QArray< QVariantList > | |
int rowCount(const QModelIndex & parent = QModelIndex()) const; | |
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; | |
bool hasIndex(int row, int column, const QModelIndex & parent = QModelIndex()) const; | |
QHash<int, QByteArray> roleNames() const; | |
/* | |
bool canFetchMore(const QModelIndex & parent) const; | |
void fetchMore(const QModelIndex & parent); | |
*/ | |
protected: | |
void requestData( qulonglong row ); | |
protected slots: | |
void slotRequestData(); | |
signals: | |
void requestDelayChanged(); | |
void rowCountChanged(); | |
void cacheChanged(); | |
void cacheSizeChanged(); | |
void recordsRequested( qulonglong first, qulonglong last ); | |
}; | |
#endif // CALLBACKMODEL_H | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TEMPLATE = lib | |
TARGET = CallbackModel | |
QT += qml quick | |
CONFIG += qt plugin | |
TARGET = $$qtLibraryTarget($$TARGET) | |
uri = com.foxmoxie.CallbackModel | |
# Input | |
SOURCES += \ | |
callbackmodel_plugin.cpp \ | |
callbackmodel.cpp | |
HEADERS += \ | |
callbackmodel_plugin.h \ | |
callbackmodel.h | |
OTHER_FILES = qmldir | |
!equals(_PRO_FILE_PWD_, $$OUT_PWD) { | |
copy_qmldir.target = $$OUT_PWD/qmldir | |
copy_qmldir.depends = $$_PRO_FILE_PWD_/qmldir | |
copy_qmldir.commands = $(COPY_FILE) \"$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)\" | |
QMAKE_EXTRA_TARGETS += copy_qmldir | |
PRE_TARGETDEPS += $$copy_qmldir.target | |
} | |
qmldir.files = qmldir | |
unix|win32 { | |
installPath = $$[QT_INSTALL_QML]/$$replace(uri, \\., /) | |
qmldir.path = $$installPath | |
target.path = $$installPath | |
INSTALLS += target qmldir | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "callbackmodel_plugin.h" | |
#include "callbackmodel.h" | |
#include <qqml.h> | |
void CallbackModelPlugin::registerTypes(const char *uri) | |
{ | |
// @uri com.foxmoxie.CallbackModel | |
qmlRegisterType<CallbackModel>(uri, 1, 0, "CallbackModel"); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CALLBACKMODEL_PLUGIN_H | |
#define CALLBACKMODEL_PLUGIN_H | |
#include <QQmlExtensionPlugin> | |
class CallbackModelPlugin : public QQmlExtensionPlugin | |
{ | |
Q_OBJECT | |
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") | |
public: | |
void registerTypes(const char *uri); | |
}; | |
#endif // CALLBACKMODEL_PLUGIN_H | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CallbackModel { | |
id: gridModel | |
property string catToken: '' | |
property string prodToken: '' | |
property int catCount: 0 | |
property int prodCount: 0 | |
requestDelay: 50 | |
onRecordsRequested: { | |
// Might be categories, might be products: | |
if( first < catCount ) | |
{ | |
socket.transmit( { 'module': 'products', 'function': 'query', 'action':'fetch', 'query':catToken, 'start':first, 'end':last } ); | |
} else // Subtract category count prefixing product items. | |
socket.transmit( { 'module': 'products', 'function': 'query', 'action':'fetch', 'query':prodToken, 'start':first-catCount, 'end':last-catCount } ); | |
} | |
function clear() | |
{ | |
// If there's a token, inform the server that it can discard it now: | |
if( catToken.length > 0 ) | |
socket.transmit( { 'module': 'products', 'function': 'query', 'action':'discard', 'query':catToken } ); | |
if( prodToken.length > 0 ) | |
socket.transmit( { 'module': 'products', 'function': 'query', 'action':'discard', 'query':prodToken } ); | |
prodToken = ''; | |
catToken = ''; | |
catCount = 0; | |
prodCount = 0; | |
gridModel.rows = 0; | |
} | |
} | |
function requestCategory() | |
{ | |
gridModel.clear(); | |
socket.transmit( { 'module': 'products', 'function': 'getCategory', 'id': categoryId } ); | |
socket.transmit( { 'module': 'products', 'function': 'getCategoryChildren', 'id': categoryId } ); | |
socket.transmit( { 'module': 'products', 'function': 'getCategoryProducts', 'id': categoryId, 'deferred':true, 'preroll':10 } ); | |
} | |
function handlePacket(packet) | |
{ | |
if( packet['module'] !== 'products' ) | |
return false; | |
if( packet['id'] !== categoryId ) | |
return false; | |
if( packet['function'] == 'getCategoryChildren' ) | |
{ | |
if( !packet['categories'] ) | |
return true; | |
if( packet['query'] ) | |
gridModel.catToken = packet['query']; | |
if( packet['total'] ) | |
gridModel.catCount = packet['total']; | |
else | |
gridModel.catCount = packet['categories'].length; | |
var firstIndex = 0; | |
if( packet['categories'] && packet['categories'].length && packet['categories'].length > 0 ) | |
{ | |
if( packet['categories'][0]['index'] ) | |
firstIndex = packet['categories'][0]['index']; | |
} | |
var entries = []; | |
for( var x=0; x < packet['categories'].length; x++ ) | |
{ | |
var entry = { 'type': 'category', 'entry': packet['categories'][x] }; | |
entries.push( entry ); | |
} | |
if( entries && entries.length > 0 ) | |
gridModel.setRecords( firstIndex, entries ); | |
gridModel.rows = gridModel.catCount + gridModel.prodCount; | |
} | |
else if( packet['function'] == 'getCategoryProducts' ) | |
{ | |
if( !packet['products'] ) | |
return true; | |
gridModel.prodToken = packet['query']; | |
if( packet['total'] ) | |
gridModel.prodCount = packet['total']; | |
else | |
gridModel.prodCount = packet['products'].length; | |
var firstIndex = 0; | |
if( packet['products'] && packet['products'].length && packet['products'].length > 0 ) | |
firstIndex = packet['products'][0]['index']; | |
var entries = []; | |
for( var x=0; x < packet['products'].length; x++ ) | |
{ | |
var entry = { 'type': 'product', 'entry': packet['products'][x] }; | |
entries.push( entry ); | |
} | |
// Offset from categories kthx: | |
firstIndex += gridModel.catCount; | |
console.log("Adding "+entries.length+" products..."); | |
if( entries && entries.length > 0 ) | |
gridModel.setRecords( firstIndex, entries ); | |
gridModel.rows = gridModel.catCount + gridModel.prodCount; | |
console.log( "Category count: "+gridModel.catCount+" / Product count: "+gridModel.prodCount); | |
} | |
else if( packet['function'] == 'getCategory' ) | |
{ | |
flipchart.parentId = packet['category']['parentId']; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment