
commit
1ac6367f25
22 changed files with 1530 additions and 0 deletions
@ -0,0 +1,8 @@
|
||||
*.pro.user |
||||
Makefile |
||||
moc_* |
||||
*.o |
||||
*.so.* |
||||
ui_* |
||||
*.so |
||||
qcp/qcp |
@ -0,0 +1,16 @@
|
||||
# ------------------------------------------------- |
||||
# Project created by QtCreator 2010-07-21T19:59:16 |
||||
# ------------------------------------------------- |
||||
QT += network |
||||
|
||||
# QT -= gui |
||||
TARGET = qcp |
||||
TEMPLATE = lib |
||||
DEFINES += LIBQCP_LIBRARY |
||||
SOURCES += qcp.cpp \ |
||||
qcpmodel.cpp |
||||
HEADERS += qcp.h \ |
||||
libqcp_global.h \ |
||||
qcpmodel.h |
||||
TARGETDEPS += ../libqtavahi/libqtavahi.so |
||||
LIBS += ../libqtavahi/libqtavahi.so |
@ -0,0 +1,12 @@
|
||||
#ifndef LIBQCP_GLOBAL_H |
||||
#define LIBQCP_GLOBAL_H |
||||
|
||||
#include <QtCore/qglobal.h> |
||||
|
||||
#if defined(LIBQCP_LIBRARY) |
||||
# define LIBQCPSHARED_EXPORT Q_DECL_EXPORT |
||||
#else |
||||
# define LIBQCPSHARED_EXPORT Q_DECL_IMPORT |
||||
#endif |
||||
|
||||
#endif // LIBQCP_GLOBAL_H
|
@ -0,0 +1,61 @@
|
||||
#include "qcp.h" |
||||
|
||||
#define SERVICETYPE "_qcp._tcp" |
||||
|
||||
namespace QCP { |
||||
Browse::Browse(QObject *parent) |
||||
: QObject(parent), m_av_browse(new Avahi::Browse(SERVICETYPE, this)) { |
||||
connect(m_av_browse, SIGNAL(found(Avahi::Service)), this, SLOT(foundService(Avahi::Service))); |
||||
connect(m_av_browse, SIGNAL(lost(Avahi::Service)), this, SLOT(lostService(Avahi::Service))); |
||||
connect(m_av_browse, SIGNAL(started()), this, SIGNAL(started())); |
||||
connect(m_av_browse, SIGNAL(stopped()), this, SIGNAL(stopped())); |
||||
} |
||||
|
||||
WatchShare *Browse::watch(const Avahi::Service &service, QObject *parent) { |
||||
ShareData *sd = shareData(service); |
||||
WatchShare *ws; |
||||
if (!sd) return 0; |
||||
|
||||
sd->watcherList.append(ws = new WatchShare(parent, Share(this, service))); |
||||
ws->m_list_it = sd->watcherList.end()-1; |
||||
|
||||
return ws; |
||||
} |
||||
|
||||
void Browse::removeWatch(WatcherList::iterator it, const Avahi::Service &service) { |
||||
ShareData *sd = shareData(service); |
||||
if (!sd || WatcherList::iterator() == it) return; |
||||
|
||||
sd->watcherList.erase(it); |
||||
} |
||||
|
||||
void Browse::foundService(Avahi::Service s) { |
||||
ShareData *sd = new ShareData(); |
||||
s.setData(sd); |
||||
emit found(Share(this, s)); |
||||
} |
||||
|
||||
void Browse::lostService(Avahi::Service s) { |
||||
emit lost(Share(this, s)); |
||||
ShareData *sd = shareData(s); |
||||
if (0 != sd) { |
||||
while (!sd->watcherList.empty()) { |
||||
WatchShare *ws = sd->watcherList.first(); |
||||
emit ws->lost(); |
||||
if (ws == sd->watcherList.first()) { |
||||
ws->m_list_it = WatcherList::iterator(); |
||||
sd->watcherList.pop_front(); |
||||
} |
||||
} |
||||
delete sd; |
||||
} |
||||
sd->data = 0; |
||||
} |
||||
|
||||
|
||||
WatchShare *Share::watch(QObject *parent) { |
||||
return m_browse->watch(m_service, parent); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,107 @@
|
||||
#ifndef QCP_H |
||||
#define QCP_H |
||||
|
||||
#include "libqcp_global.h" |
||||
|
||||
#include <QObject> |
||||
#include <QLinkedList> |
||||
|
||||
#include "../libqtavahi/qtavahi.h" |
||||
|
||||
namespace QCP { |
||||
class LIBQCPSHARED_EXPORT Share; |
||||
class LIBQCPSHARED_EXPORT WatchShare; |
||||
class LIBQCPSHARED_EXPORT Browse; |
||||
} |
||||
|
||||
namespace QCP { |
||||
/* The private data of the Avahi::Service instances belongs to QCP ! :) */ |
||||
|
||||
class LIBQCPSHARED_EXPORT Browse : public QObject { |
||||
Q_OBJECT |
||||
private: |
||||
friend class QCP::Share; |
||||
friend class QCP::WatchShare; |
||||
|
||||
typedef QLinkedList<WatchShare*> WatcherList; |
||||
struct ShareData { |
||||
WatcherList watcherList; |
||||
void *data; |
||||
}; |
||||
|
||||
public: |
||||
Browse(QObject *parent = 0); |
||||
|
||||
Avahi::Browse* avahiBrowse() { return m_av_browse; } |
||||
|
||||
inline bool start() { return m_av_browse->start(); } |
||||
inline void stop() { m_av_browse->stop(); } |
||||
|
||||
signals: |
||||
void found(QCP::Share s); |
||||
void lost(QCP::Share s); |
||||
|
||||
void started(); |
||||
void stopped(); |
||||
|
||||
private slots: |
||||
void foundService(Avahi::Service s); |
||||
void lostService(Avahi::Service s); |
||||
|
||||
private: |
||||
WatchShare *watch(const Avahi::Service &service, QObject *parent); |
||||
void removeWatch(WatcherList::iterator it, const Avahi::Service &service); |
||||
|
||||
static inline ShareData* shareData(const Avahi::Service &service) { |
||||
return static_cast<ShareData*>(service.data()); |
||||
} |
||||
|
||||
Avahi::Browse *m_av_browse; |
||||
}; |
||||
|
||||
class LIBQCPSHARED_EXPORT Share { |
||||
private: |
||||
friend class QCP::Browse; |
||||
friend class QCP::WatchShare; |
||||
|
||||
public: |
||||
inline Share(QCP::Browse *browse, Avahi::Service service) : m_browse(browse), m_service(service) { } |
||||
|
||||
public: |
||||
Avahi::Service avahiService() const { return m_service; } |
||||
|
||||
WatchShare *watch(QObject *parent = 0); |
||||
|
||||
void* data() const { Browse::ShareData *sd = Browse::shareData(m_service); return sd ? sd->data : 0; } |
||||
void setData(void *data) const { Browse::ShareData *sd = Browse::shareData(m_service); if (sd) sd->data = data; } |
||||
|
||||
private: |
||||
QCP::Browse *m_browse; |
||||
Avahi::Service m_service; |
||||
}; |
||||
|
||||
class LIBQCPSHARED_EXPORT WatchShare : public QObject { |
||||
Q_OBJECT |
||||
|
||||
friend class QCP::Share; |
||||
friend class QCP::Browse; |
||||
private: |
||||
inline WatchShare(QObject *parent, Share share) : QObject(parent), m_share(share) { } |
||||
|
||||
public: |
||||
virtual ~WatchShare() { |
||||
m_share.m_browse->removeWatch(m_list_it, m_share.m_service); |
||||
} |
||||
|
||||
inline Share share() { return m_share; } |
||||
|
||||
signals: |
||||
void lost(); |
||||
|
||||
private: |
||||
Share m_share; |
||||
Browse::WatcherList::iterator m_list_it; |
||||
}; |
||||
} |
||||
|
||||
#endif // QCP_H
|
@ -0,0 +1,91 @@
|
||||
#include "qcpmodel.h" |
||||
|
||||
namespace QCP { |
||||
|
||||
namespace model { |
||||
Entry::Entry(Row *parent, const Avahi::Service &s, const QCP::Share &share) |
||||
: Avahi::model::Entry(parent, s), m_share(share) { |
||||
} |
||||
|
||||
QVariant Entry::data(int col, int role) { |
||||
switch (role) { |
||||
case Qt::DisplayRole: |
||||
switch (col) { |
||||
case 0: return service.hostname(); |
||||
case 1: return QString("%1:%2").arg(service.address().toString()).arg(service.port()); |
||||
case 2: return "Hello World!"; |
||||
} |
||||
// case Qt::UserRole
|
||||
default: |
||||
return QVariant(); |
||||
} |
||||
return QVariant(); |
||||
} |
||||
|
||||
EntryList::EntryList(Row *parent, const Avahi::ServiceKey &k) |
||||
: Avahi::model::EntryList(parent, k) { } |
||||
|
||||
int EntryList::columnCount() { |
||||
return 3; |
||||
} |
||||
|
||||
QVariant EntryList::data(int col, int role) { |
||||
switch (role) { |
||||
case Qt::DisplayRole: |
||||
switch (col) { |
||||
case 0: return key.name; |
||||
} |
||||
// case Qt::UserRole
|
||||
default: |
||||
return QVariant(); |
||||
} |
||||
return QVariant(); |
||||
} |
||||
|
||||
Avahi::model::Entry* EntryList::newEntry(const Avahi::Service &s, void *priv) { |
||||
QCP::Share *share = static_cast< QCP::Share* >(priv); |
||||
return new Entry(this, s, *share); |
||||
} |
||||
|
||||
|
||||
int Root::columnCount() { |
||||
return 3; |
||||
} |
||||
|
||||
EntryList* Root::newList(const Avahi::ServiceKey &k) { |
||||
return new EntryList(this, k); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
BrowseModel::BrowseModel(QObject *parent) |
||||
: Avahi::BrowseModel(new QCP::model::Root(), parent), m_browse(new QCP::Browse(this)) { |
||||
connect(m_browse, SIGNAL(found(QCP::Share)), this, SLOT(foundShare(QCP::Share))); |
||||
connect(m_browse, SIGNAL(lost(QCP::Share)), this, SLOT(lostShare(QCP::Share))); |
||||
|
||||
m_browse->start(); |
||||
} |
||||
|
||||
QVariant BrowseModel::headerData(int section, Qt::Orientation orientation, int role) const { |
||||
if (orientation != Qt::Horizontal) return QVariant(); |
||||
switch (role) { |
||||
case Qt::DisplayRole: |
||||
switch (section) { |
||||
case 0: return "Name"; |
||||
case 1: return "Address"; |
||||
case 2: return "Version"; |
||||
} |
||||
break; |
||||
} |
||||
return QVariant(); |
||||
} |
||||
|
||||
void BrowseModel::foundShare(QCP::Share s) { |
||||
insert(s.avahiService(), &s); |
||||
} |
||||
|
||||
void BrowseModel::lostShare(QCP::Share s) { |
||||
remove(s.avahiService()); |
||||
} |
||||
} |
@ -0,0 +1,59 @@
|
||||
#ifndef QCPMODEL_H |
||||
#define QCPMODEL_H |
||||
|
||||
#include "qcp.h" |
||||
#include "../libqtavahi/qtavahimodel.h" |
||||
|
||||
namespace QCP { |
||||
namespace model { |
||||
class LIBQCPSHARED_EXPORT Entry : public Avahi::model::Entry { |
||||
public: |
||||
Entry(Row *parent, const Avahi::Service &s, const QCP::Share &share); |
||||
|
||||
virtual QVariant data(int col, int role); |
||||
|
||||
protected: |
||||
QCP::Share m_share; |
||||
}; |
||||
|
||||
class LIBQCPSHARED_EXPORT EntryList : public Avahi::model::EntryList { |
||||
public: |
||||
EntryList(Row *parent, const Avahi::ServiceKey &k); |
||||
|
||||
virtual int columnCount(); |
||||
|
||||
virtual QVariant data(int col, int role); |
||||
|
||||
protected: |
||||
virtual Avahi::model::Entry* newEntry(const Avahi::Service &s, void *priv); |
||||
}; |
||||
|
||||
class LIBQCPSHARED_EXPORT Root : public Avahi::model::Root { |
||||
public: |
||||
virtual int columnCount(); |
||||
|
||||
protected: |
||||
virtual EntryList* newList(const Avahi::ServiceKey &k); |
||||
}; |
||||
} |
||||
|
||||
class LIBQCPSHARED_EXPORT BrowseModel : public Avahi::BrowseModel { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
BrowseModel(QObject *parent = 0); |
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const; |
||||
|
||||
QCP::Browse *browse() { return m_browse; } |
||||
|
||||
protected slots: |
||||
void foundShare(QCP::Share s); |
||||
void lostShare(QCP::Share s); |
||||
|
||||
private: |
||||
QCP::Browse *m_browse; |
||||
}; |
||||
} |
||||
|
||||
#endif // QCPMODEL_H
|
@ -0,0 +1,17 @@
|
||||
# ------------------------------------------------- |
||||
# Project created by QtCreator 2010-07-23T21:04:59 |
||||
# ------------------------------------------------- |
||||
QT += network |
||||
QT -= gui |
||||
TARGET = qtavahi |
||||
TEMPLATE = lib |
||||
DEFINES += LIBQTAVAHI_LIBRARY |
||||
SOURCES += qtavahi.cpp \ |
||||
qtavahimodel.cpp |
||||
HEADERS += qtavahi.h \ |
||||
libqtavahi_global.h \ |
||||
qtavahi_p.h \ |
||||
qtavahimodel.h |
||||
LIBS += -lavahi-common \ |
||||
-lavahi-client \ |
||||
-lavahi-qt4 |
@ -0,0 +1,12 @@
|
||||
#ifndef LIBQTAVAHI_GLOBAL_H |
||||
#define LIBQTAVAHI_GLOBAL_H |
||||
|
||||
#include <QtCore/qglobal.h> |
||||
|
||||
#if defined(LIBQTAVAHI_LIBRARY) |
||||
# define LIBQTAVAHISHARED_EXPORT Q_DECL_EXPORT |
||||
#else |
||||
# define LIBQTAVAHISHARED_EXPORT Q_DECL_IMPORT |
||||
#endif |
||||
|
||||
#endif // LIBQTAVAHI_GLOBAL_H
|
@ -0,0 +1,563 @@
|
||||
|
||||
#include "qtavahi.h" |
||||
|
||||
#include <avahi-client/client.h> |
||||
#include <avahi-client/lookup.h> |
||||
#include <avahi-client/publish.h> |
||||
|
||||
#include <avahi-common/alternative.h> |
||||
#include <avahi-common/error.h> |
||||
#include <avahi-common/malloc.h> |
||||
|
||||
#include <avahi-qt4/qt-watch.h> |
||||
|
||||
#include <QSize> |
||||
#include <QtEndian> |
||||
|
||||
#include <netinet/in.h> |
||||
|
||||
namespace Avahi { |
||||
|
||||
static QHostAddress fromAvahiAddress(const AvahiAddress *address, AvahiIfIndex interface) { |
||||
switch (address->proto) { |
||||
case AVAHI_PROTO_INET: |
||||
return QHostAddress(qFromBigEndian((quint32) address->data.ipv4.address)); |
||||
case AVAHI_PROTO_INET6: { |
||||
struct sockaddr_in6 a; |
||||
memset(&a, 0, sizeof(a)); |
||||
a.sin6_family = AF_INET6; |
||||
a.sin6_scope_id = interface; |
||||
memcpy(a.sin6_addr.s6_addr, address->data.ipv6.address, 16); |
||||
return QHostAddress((const sockaddr*) &a); |
||||
} |
||||
case AVAHI_PROTO_UNSPEC: |
||||
return QHostAddress(); |
||||
} |
||||
return QHostAddress(); |
||||
} |
||||
|
||||
namespace priv { |
||||
|
||||
static void browsep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata); |
||||
static void browsep_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, |
||||
const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata); |
||||
static void browsep_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, |
||||
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, |
||||
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata); |
||||
} |
||||
} |
||||
|
||||
namespace Avahi { |
||||
namespace priv { |
||||
|
||||
class BrowseP { |
||||
public: |
||||
static const ::AvahiPoll *m_qtpoll; |
||||
|
||||
QString m_serviceType; |
||||
|
||||
bool m_initialized; |
||||
bool m_running; |
||||
|
||||
::AvahiClient *m_client; |
||||
::AvahiServiceBrowser *m_browser; |
||||
|
||||
QByteArray m_service_type; |
||||
|
||||
Avahi::Browse *m_parent; |
||||
|
||||
QHash<ServiceKey, Service> m_list; |
||||
|
||||
BrowseP(QString servicetype, Browse *parent) |
||||
: m_serviceType(servicetype), |
||||
m_initialized(false), m_running(false), m_client(0), m_browser(0), |
||||
m_service_type(servicetype.toUtf8()), m_parent(parent) { |
||||
int err; |
||||
|
||||
if (!m_qtpoll) { |
||||
if (!(m_qtpoll = avahi_qt_poll_get())) { |
||||
qWarning("avahi_qt_poll_get failed"); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
if (!(m_client = avahi_client_new(m_qtpoll, (AvahiClientFlags) 0, browsep_client_callback, this, &err))) { |
||||
qWarning("avahi_client_new failed: %s", avahi_strerror(err)); |
||||
return; |
||||
} |
||||
|
||||
m_initialized = true; |
||||
} |
||||
|
||||
void clearList() { |
||||
QList<Service> l = m_list.values(); |
||||
m_list.clear(); |
||||
for (int i = 0; i < l.size(); ++i) { |
||||
emit m_parent->lost(l.at(i)); |
||||
} |
||||
} |
||||
|
||||
void release() { |
||||
stop(); |
||||
|
||||
if (!m_initialized) return; |
||||
|
||||
if (m_client) { |
||||
avahi_client_free(m_client); |
||||
m_client = 0; |
||||
} |
||||
|
||||
m_initialized = false; |
||||
} |
||||
|
||||
~BrowseP() { |
||||
release(); |
||||
} |
||||
|
||||
void client_callback(AvahiClient *c, AvahiClientState state) { |
||||
switch (state) { |
||||
case AVAHI_CLIENT_S_RUNNING: |
||||
break; |
||||
case AVAHI_CLIENT_FAILURE: |
||||
qWarning("avahi client failure: %s", avahi_strerror(avahi_client_errno(c))); |
||||
release(); |
||||
break; |
||||
case AVAHI_CLIENT_S_COLLISION: |
||||
case AVAHI_CLIENT_S_REGISTERING: |
||||
break; |
||||
case AVAHI_CLIENT_CONNECTING: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, |
||||
const char *name, const char *type, const char *domain, AvahiLookupResultFlags /* flags */) { |
||||
if (!m_browser) m_browser = b; |
||||
Q_ASSERT(m_browser == b); |
||||
|
||||
if (protocol != AVAHI_PROTO_INET && protocol != AVAHI_PROTO_INET6) return; /* ignore non IPv[46] for now */ |
||||
|
||||
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */ |
||||
|
||||
switch (event) { |
||||
case AVAHI_BROWSER_FAILURE: |
||||
qWarning("(Browser) %s", avahi_strerror(avahi_client_errno(m_client))); |
||||
release(); |
||||
return; |
||||
|
||||
case AVAHI_BROWSER_NEW: |
||||
qDebug("(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain); |
||||
|
||||
/* We ignore the returned resolver object. In the callback
|
||||
* function we free it. If the server is terminated before |
||||
* the callback function is called the server will free |
||||
* the resolver for us. */ |
||||
|
||||
if (!(avahi_service_resolver_new(m_client, interface, protocol, name, type, domain, protocol, (AvahiLookupFlags) 0, browsep_resolve_callback, this))) { |
||||
qWarning("Failed to resolve service '%s' on '%s': %s", name, domain, avahi_strerror(avahi_client_errno(m_client))); |
||||
} |
||||
|
||||
break; |
||||
|
||||
case AVAHI_BROWSER_REMOVE: { |
||||
ServiceKey k(interface, protocol, name, domain); |
||||
Service s = m_list.value(k); |
||||
if (s.valid()) { |
||||
m_list.remove(k); |
||||
emit m_parent->lost(s); |
||||
} |
||||
} |
||||
break; |
||||
|
||||
case AVAHI_BROWSER_ALL_FOR_NOW: |
||||
break; |
||||
case AVAHI_BROWSER_CACHE_EXHAUSTED: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, |
||||
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, |
||||
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags /* flags */) { |
||||
Q_ASSERT(r); |
||||
|
||||
// if (protocol != AVAHI_PROTO_INET) goto done; /* ignore non IPv4 for now */
|
||||
if (protocol != AVAHI_PROTO_INET && protocol != AVAHI_PROTO_INET6) goto done; /* ignore non IPv[46] for now */ |
||||
|
||||
/* Called whenever a service has been resolved successfully or timed out */ |
||||
|
||||
switch (event) { |
||||
case AVAHI_RESOLVER_FAILURE: |
||||
qDebug("(Resolver) Failed to resolve service '%s' of type '%s' at '%s' in domain '%s': %s", name, type, host_name, domain, avahi_strerror(avahi_client_errno(m_client))); |
||||
break; |
||||
|
||||
case AVAHI_RESOLVER_FOUND: { |
||||
qDebug("(Resolver) found service '%s' of type '%s' at '%s' in domain '%s'", name, type, host_name, domain); |
||||
QHash<QString, QString> qtxt; |
||||
AvahiStringList *psl; |
||||
for (psl = txt ; psl ; psl = psl->next) { |
||||
QString e = QString::fromUtf8((const char*) psl->text, psl->size), key, value; |
||||
int eqPos = e.indexOf(QChar('"')); |
||||
if (eqPos == 0) continue; /* ignore empty keys */ |
||||
if (eqPos < -1) { /* "boolean" */ |
||||
key = e; |
||||
} else { |
||||
key = e.left(eqPos - 1); |
||||
value = e.mid(eqPos + 1); |
||||
} |
||||
key = key.toLower(); |
||||
qtxt.insert(key, value); |
||||
} |
||||
|
||||
ServiceKey k(interface, protocol, name, domain); |
||||
Service s = m_list.value(k); |
||||
if (s.valid()) { |
||||
m_list.remove(k); |
||||
emit m_parent->lost(s); |
||||
} |
||||
|
||||
s = Service(QString::fromUtf8(name), QString::fromUtf8(host_name), QString::fromUtf8(domain), fromAvahiAddress(address, interface), interface, protocol, port, qtxt); |
||||
m_list.insert(k, s); |
||||
emit m_parent->found(s); |
||||
} |
||||
} |
||||
|
||||
done: |
||||
avahi_service_resolver_free(r); |
||||
} |
||||
|
||||
bool start() { |
||||
if (m_running) return true; |
||||
if (!m_initialized) return false; |
||||
|
||||
clearList(); |
||||
|
||||
if (NULL == (m_browser = avahi_service_browser_new(m_client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, m_service_type.constData(), NULL, (AvahiLookupFlags) 0, browsep_browse_callback, this))) { |
||||
qWarning("Failed to create service browser: %s", avahi_strerror(avahi_client_errno(m_client))); |
||||
return false; |
||||
} |
||||
|
||||
emit m_parent->started(); |
||||
m_running = true; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void stop() { |
||||
clearList(); |
||||
if (m_running) { |
||||
emit m_parent->stopped(); |
||||
m_running = false; |
||||
} |
||||
if (m_browser) { |
||||
avahi_service_browser_free(m_browser); |
||||
m_browser = 0; |
||||
} |
||||
} |
||||
}; |
||||
const AvahiPoll *BrowseP::m_qtpoll = 0; |
||||
|
||||
static void browsep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata) { |
||||
BrowseP *p = static_cast<BrowseP*>(userdata); |
||||
p->client_callback(c, state); |
||||
} |
||||
|
||||
static void browsep_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, |
||||
const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata) { |
||||
BrowseP *p = static_cast<BrowseP*>(userdata); |
||||
p->browse_callback(b, interface, protocol, event, name, type, domain, flags); |
||||
} |
||||
|
||||
static void browsep_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, |
||||
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, |
||||
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) { |
||||
BrowseP *p = static_cast<BrowseP*>(userdata); |
||||
p->resolve_callback(r, interface, protocol, event, name, type, domain, host_name, address, port, txt, flags); |
||||
} |
||||
|
||||
static void announcep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata); |
||||
static void announcep_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata); |
||||
|
||||
class AnnounceP { |
||||
public: |
||||
static const AvahiPoll *m_qtpoll; |
||||
|
||||
QString m_serviceType, m_name; |
||||
quint16 m_port; |
||||
QHash<QString,QString> m_txt; |
||||
|
||||
bool m_initialized; |
||||
bool m_running; |
||||
|
||||
AvahiClient *m_client; |
||||
AvahiEntryGroup *m_group; |
||||
|
||||
QByteArray m_service_type; |
||||
|
||||
Announce *m_parent; |
||||
|
||||
char *m_raw_name; /* avahi string */ |
||||
QList<QByteArray> m_txt_list; |
||||
::AvahiStringList *m_raw_txt; |
||||
|
||||
AnnounceP(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, Announce *parent) |
||||
: m_serviceType(serviceType), m_name(name), m_port(port), m_txt(txt), |
||||
m_initialized(false), m_running(false), m_client(0), m_group(0), |
||||
m_service_type(serviceType.toUtf8()), m_parent(parent), |
||||
m_raw_name(avahi_strdup(name.toUtf8().constData())), m_raw_txt(0) { |
||||
int err; |
||||
|
||||
QHashIterator<QString, QString> i(m_txt); |
||||
while (i.hasNext()) { |
||||
QByteArray a; |
||||
i.next(); |
||||
if (i.value().isNull()) { |
||||
m_txt_list.append(a = i.key().toUtf8()); |
||||
} else { |
||||
m_txt_list.append(a = QString("%1=%2").arg(i.key(), i.value()).toUtf8()); |
||||
} |
||||
AvahiStringList *l = avahi_string_list_new(a.constData(), NULL); |
||||
l->next = m_raw_txt; |
||||
m_raw_txt = l; |
||||
} |
||||
|
||||
if (!m_qtpoll) { |
||||
if (!(m_qtpoll = avahi_qt_poll_get())) { |
||||
qWarning("avahi_qt_poll_get failed"); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
if (!(m_client = avahi_client_new(m_qtpoll, (AvahiClientFlags) 0, announcep_client_callback, this, &err))) { |
||||
qWarning("avahi_client_new failed: %s", avahi_strerror(err)); |
||||
return; |
||||
} |
||||
|
||||
m_initialized = true; |
||||
|
||||
start(); |
||||
} |
||||
|
||||
void release() { |
||||
if (m_running) { |
||||
m_running = false; |
||||
} |
||||
if (!m_initialized) return; |
||||
|
||||
if (m_group) { |
||||
qDebug("avahi unregister service"); |
||||
avahi_entry_group_reset(m_group); |
||||
avahi_entry_group_free(m_group); |
||||
m_group = 0; |
||||
} |
||||
|
||||
if (m_client) { |
||||
avahi_client_free(m_client); |
||||
m_client = 0; |
||||
} |
||||
|
||||
m_initialized = false; |
||||
|
||||
emit m_parent->stopped(); |
||||
} |
||||
|
||||
~AnnounceP() { |
||||
release(); |
||||
if (m_raw_name) { |
||||
avahi_free(m_raw_name); |
||||
m_raw_name = 0; |
||||
} |
||||
if (m_raw_txt) { |
||||
avahi_string_list_free(m_raw_txt); |
||||
m_raw_txt = 0; |
||||
} |
||||
} |
||||
|
||||
void client_callback(AvahiClient *c, AvahiClientState state) { |
||||
m_client = c; |
||||
switch (state) { |
||||
case AVAHI_CLIENT_S_RUNNING: |
||||
create_services(); |
||||
break; |
||||
case AVAHI_CLIENT_FAILURE: |
||||
qDebug("avahi client failure"); |
||||
release(); |
||||
break; |
||||
case AVAHI_CLIENT_S_COLLISION: |
||||
case AVAHI_CLIENT_S_REGISTERING: |
||||
if (m_group) { |
||||
avahi_entry_group_reset(m_group); |
||||
} |
||||
break; |
||||
case AVAHI_CLIENT_CONNECTING: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state) { |
||||
m_group = g; |
||||
|
||||
switch (state) { |
||||
case AVAHI_ENTRY_GROUP_ESTABLISHED : |
||||
emit m_parent->established(); |
||||
break; |
||||
|
||||
case AVAHI_ENTRY_GROUP_COLLISION : |
||||
{ |
||||
char *n = avahi_alternative_service_name(m_raw_name); |
||||
avahi_free(m_raw_name); |
||||
m_raw_name = n; |
||||
m_name = QString::fromUtf8(m_raw_name); |
||||
} |
||||
|
||||
/* And recreate the services */ |
||||
avahi_entry_group_reset(m_group); |
||||
create_services(); |
||||
/* same name should result in AVAHI_ERR_COLLISION in create_services, emit collision there. */ |
||||
break; |
||||
|
||||
case AVAHI_ENTRY_GROUP_FAILURE : |
||||
qDebug("Entry group failure: %s", avahi_strerror(avahi_client_errno(m_client))); |
||||
|
||||
release(); |
||||
break; |
||||
|
||||
case AVAHI_ENTRY_GROUP_UNCOMMITED: |
||||
case AVAHI_ENTRY_GROUP_REGISTERING: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
bool create_services() { |
||||
int ret; |
||||
|
||||
if (!m_group) { |
||||
if (!(m_group = avahi_entry_group_new(m_client, announcep_entry_group_callback, this))) { |
||||
qDebug("avahi_entry_group_new failed: %s", avahi_strerror(avahi_client_errno(m_client))); |
||||
release(); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
again: |
||||
if (avahi_entry_group_is_empty(m_group)) { |
||||
if ((ret = avahi_entry_group_add_service_strlst(m_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags) 0, m_raw_name, m_service_type.constData(), NULL, NULL, m_port, m_raw_txt)) < 0) { |
||||
|
||||
if (ret == AVAHI_ERR_COLLISION) |
||||
goto collision; |
||||
|
||||
qDebug("Failed to add '%s': %s", m_service_type.constData(), avahi_strerror(ret)); |
||||
release(); |
||||
return false; |
||||
} |
||||
if ((ret = avahi_entry_group_commit(m_group)) < 0) { |
||||
qDebug("Failed to commit entry group: %s", avahi_strerror(ret)); |
||||
release(); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
|
||||
collision: |
||||
qDebug("avahi name collision: %s, trying a new one", m_raw_name); |
||||
{ |
||||
char *n = avahi_alternative_service_name(m_raw_name); |
||||
avahi_free(m_raw_name); |
||||
m_raw_name = n; |
||||
m_name = QString::fromUtf8(m_raw_name); |
||||
} |
||||
avahi_entry_group_reset(m_group); |
||||
emit m_parent->collision(); |
||||
goto again; |
||||
} |
||||
|
||||
bool start() { |
||||
if (m_running) return true; |
||||
if (!m_initialized) return false; |
||||
|
||||
m_running = true; |
||||
emit m_parent->started(); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void stop() { |
||||
release(); |
||||
} |
||||
}; |
||||
const AvahiPoll *AnnounceP::m_qtpoll = 0; |
||||
|
||||
static void announcep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata) { |
||||
AnnounceP *p = static_cast<AnnounceP*>(userdata); |
||||
p->client_callback(c, state); |
||||
} |
||||
static void announcep_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { |
||||
AnnounceP *p = static_cast<AnnounceP*>(userdata); |
||||
p->entry_group_callback(g, state); |
||||
} |
||||
} |
||||
|
||||
/* public interfaces */ |
||||
|
||||
Browse::Browse(QString serviceType, QObject *parent) |
||||
: QObject(parent), m_priv(new priv::BrowseP(serviceType, this)) { |
||||
} |
||||
|
||||
Browse::~Browse() { |
||||
delete m_priv; |
||||
m_priv = 0; |
||||
} |
||||
|
||||
bool Browse::start() { |
||||
return m_priv->start(); |
||||
} |
||||
|
||||
void Browse::stop() { |
||||
m_priv->stop(); |
||||
} |
||||
|
||||
QString Browse::serviceType() const { |
||||
return m_priv->m_serviceType; |
||||
} |
||||
|
||||
QList<Service> Browse::currentList() const { |
||||
return m_priv->m_list.values(); |
||||
} |
||||
|
||||
|
||||
Announce::Announce(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent) |
||||
: QObject(parent), m_priv(new priv::AnnounceP(serviceType, name, port, txt, this)) { |
||||
} |
||||
|
||||
Announce::~Announce() { |
||||
delete m_priv; |
||||
m_priv = 0; |
||||
} |
||||
|
||||
bool Announce::start() { |
||||
return m_priv->start(); |
||||
} |
||||
|
||||
void Announce::stop() { |
||||
m_priv->stop(); |
||||
} |
||||
|
||||
QString Announce::serviceType() const { |
||||
return m_priv->m_serviceType; |
||||
} |
||||
|
||||
QString Announce::name() const { |
||||
return m_priv->m_name; |
||||
} |
||||
quint16 Announce::port() const { |
||||
return m_priv->m_port; |
||||
} |
||||
QHash<QString, QString> Announce::txt() const { |
||||
return m_priv->m_txt; |
||||
} |
||||
QString Announce::txt(QString key) const { |
||||
return m_priv->m_txt.value(key); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,186 @@
|
||||
#ifndef QTAVAHI_H |
||||
#define QTAVAHI_H |
||||
|
||||
#include <QObject> |
||||
|
||||
#include "libqtavahi_global.h" |
||||
|
||||
namespace Avahi { |
||||
class LIBQTAVAHISHARED_EXPORT Service; |
||||
class LIBQTAVAHISHARED_EXPORT ServiceKey; |
||||
class LIBQTAVAHISHARED_EXPORT Browse; |
||||
class LIBQTAVAHISHARED_EXPORT Announce; |
||||
} |
||||
|
||||
/* we need this before #include <QHash> so it finds our qHash function */ |
||||
inline uint qHash(const Avahi::ServiceKey& k); |
||||
|
||||
#include <QHostAddress> |
||||
#include <QAbstractItemModel> |
||||
#include <QAtomicInt> |
||||
#include <QString> |
||||
#include <QHash> |
||||
|
||||
#include "qtavahi_p.h" |
||||
|
||||
namespace Avahi { |
||||
|
||||
class LIBQTAVAHISHARED_EXPORT Service { |
||||
public: |
||||
inline Service() : m_priv(0) { } |
||||
inline Service(QString name, QString hostname, QString domain, QHostAddress address, int interface, int protocol, quint16 port, QHash<QString, QString> txt) |
||||
: m_priv(new Avahi::priv::ServiceP(name, hostname, domain, address, interface, protocol, port, txt)) { } |
||||
|
||||
inline Service(const Service &s) : m_priv(0) { assign(s.m_priv); } |
||||
inline Service &operator=(const Service &s) { assign(s.m_priv); return *this; } |
||||
|
||||
~Service() { assign(0); } |
||||
|
||||
inline QString name() const { return m_priv ? m_priv->m_name : QString(); } |
||||
inline QString hostname() const { return m_priv ? m_priv->m_hostname : QString(); } |
||||
inline QString domain() const { return m_priv ? m_priv->m_domain : QString(); } |
||||
inline QHostAddress address() const { return m_priv ? m_priv->m_address : QHostAddress(); } |
||||
inline quint16 port() const { return m_priv ? m_priv->m_port : 0; } |
||||
inline QHash<QString, QString> txt() const { return m_priv ? m_priv->m_txt : QHash<QString, QString>(); } |
||||
inline QString txt(QString key) const { return m_priv ? m_priv->m_txt.value(key) : QString(); } |
||||
inline bool valid() const { return 0 != m_priv; } |
||||
|
||||
/* save custom data for a service (the service is probably associated with a Browse instance, so all
|
||||
code using the same Browse instance uses the same private data here */ |
||||
inline void* data() const { return m_priv ? m_priv->m_data : 0; } |
||||
inline void setData(void *data) const { if (m_priv) m_priv->m_data = data; } |
||||
|
||||
private: |
||||
friend class Avahi::ServiceKey; |
||||
|
||||
inline void assign(Avahi::priv::ServiceP *p) { |
||||
if (p == m_priv) return; |
||||
if (p) p->ref(); |
||||
if (m_priv) m_priv->deref(); |
||||
m_priv = p; |
||||
} |
||||
|
||||
Avahi::priv::ServiceP *m_priv; |
||||
}; |
||||
|
||||
class LIBQTAVAHISHARED_EXPORT ServiceKey { |
||||
public: |
||||
inline ServiceKey(int interface, int protocol, const char *name, const char *domain) |
||||
: interface(interface), protocol(protocol), name(QString::fromUtf8(name)), domain(QString::fromUtf8(domain)) { |
||||
} |
||||
inline ServiceKey(int interface, int protocol, QString name, QString domain) |
||||
: interface(interface), protocol(protocol), name(name), domain(domain) { |
||||
} |
||||
inline ServiceKey(const Service &s) |
||||
: interface(-1), protocol(-1), name(), domain() { |
||||
if (s.m_priv) { |
||||
interface = s.m_priv->m_interface; |
||||
protocol = s.m_priv->m_protocol; |
||||
name = s.m_priv->m_name; |
||||
domain = s.m_priv->m_domain; |
||||
} |
||||
} |
||||
|
||||
int interface; /* AvahiIfIndex */ |
||||
int protocol; /* AvahiProtocol */ |
||||
QString name; |
||||
QString domain; |
||||
}; |
||||
|
||||
inline bool operator==(const ServiceKey &a, const ServiceKey &b) { |
||||
return a.interface == b.interface && a.protocol == b.protocol && a.name == b.name && a.domain == b.domain; |
||||
} |
||||
inline bool operator<(const ServiceKey &a, const ServiceKey &b) { |
||||
if (a.interface < b.interface) return true; |
||||
if (a.interface > b.interface) return false; |
||||
if (a.protocol < b.protocol) return true; |
||||
if (a.protocol > b.protocol) return false; |
||||
if (a.name < b.name) return true; |
||||
if (a.name > b.name) return false; |
||||
return a.domain < b.domain; |
||||
} |
||||
inline bool operator>(const ServiceKey &a, const ServiceKey &b) { return b < a; } |
||||
inline bool operator<=(const ServiceKey &a, const ServiceKey &b) { return !(b < a); } |
||||
inline bool operator>=(const ServiceKey &a, const ServiceKey &b) { return !(a < b); } |
||||
|
||||
|
||||
|
||||
class LIBQTAVAHISHARED_EXPORT Browse : public QObject { |
||||
Q_OBJECT |
||||
private: |
||||
Q_DISABLE_COPY(Browse); |
||||
|
||||
public: |
||||
typedef Avahi::priv::Protocol Protocol; |
||||
inline Browse(QString servtype, Protocol prot, QObject *parent = 0) { |
||||
Browse(Avahi::priv::serv_prot2str(servtype, prot), parent); |
||||
} |
||||
Browse(QString serviceType, QObject *parent = 0); |
||||
virtual ~Browse(); |
||||
|
||||
bool start(); |
||||
void stop(); |
||||
|
||||
QString serviceType() const; |
||||
|
||||
QList<Service> currentList() const; |
||||
|
||||
private: |
||||
void clearList(); |
||||
|
||||
signals: |
||||
void found(Avahi::Service i); |
||||
void lost(Avahi::Service i); |
||||
|
||||
void started(); |
||||
void stopped(); |
||||
|
||||
private: |
||||
Avahi::priv::BrowseP *m_priv; |
||||
|
||||
friend class Avahi::priv::BrowseP; |
||||
}; |
||||
|
||||
class LIBQTAVAHISHARED_EXPORT Announce : public QObject { |
||||
Q_OBJECT |
||||
private: |
||||
Q_DISABLE_COPY(Announce); |
||||
|
||||
public: |
||||
typedef Avahi::priv::Protocol Protocol; |
||||
inline Announce(QString servtype, Protocol prot, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent = 0) { |
||||
Announce(Avahi::priv::serv_prot2str(servtype, prot), name, port, txt, parent); |
||||
} |
||||
Announce(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent = 0); |
||||
virtual ~Announce(); |
||||
|
||||
QString serviceType() const; |
||||
QString name() const; |
||||
quint16 port() const; |
||||
QHash<QString, QString> txt() const; |
||||
QString txt(QString key) const; |
||||
|
||||
bool start(); |
||||
void stop(); |
||||
|
||||
signals: |
||||
void established(); /* the name may have been changed after collisions */ |
||||
void collision(); |
||||
void started(); |
||||
void stopped(); |
||||
|
||||
private: |
||||
Avahi::priv::AnnounceP *m_priv; |
||||
|
||||
friend class Avahi::priv::AnnounceP; |
||||
}; |
||||
|
||||
} |
||||
|
||||
QT_BEGIN_NAMESPACE |
||||
inline uint qHash(const Avahi::ServiceKey& k) { |
||||
return qHash(k.interface ^ k.protocol) ^ qHash(k.name) ^ qHash(k.domain); |
||||
} |
||||
QT_END_NAMESPACE |
||||
|
||||
#endif // QTAVAHI_H
|
@ -0,0 +1,50 @@
|
||||
#ifndef QTAVAHI_P_H |
||||
#define QTAVAHI_P_H |
||||
|
||||
namespace Avahi { |
||||
|
||||
namespace priv { |
||||
class LIBQTAVAHISHARED_EXPORT ServiceP; |
||||
class BrowseP; |
||||
class AnnounceP; |
||||
}; |
||||
} |
||||
|
||||
namespace Avahi { |
||||
namespace priv { |
||||
class LIBQTAVAHISHARED_EXPORT ServiceP { |
||||
private: |
||||
friend class Avahi::Service; |
||||
friend class Avahi::ServiceKey; |
||||
friend class Avahi::Browse; |
||||
inline ServiceP(QString name, QString hostname, QString domain, QHostAddress address, int interface, int protocol, quint16 port, QHash<QString, QString> txt) |
||||
: m_ref(1), m_name(name), m_hostname(hostname), m_domain(domain), m_address(address), m_interface(interface), m_protocol(protocol), m_port(port), m_txt(txt), m_data(0) { } |
||||
|
||||
Q_DISABLE_COPY(ServiceP); |
||||
|
||||
inline void ref() { m_ref.ref(); } |
||||
inline void deref() { if (!m_ref.deref()) { delete this; } } |
||||
|
||||
QAtomicInt m_ref; |
||||
|
||||
QString m_name, m_hostname, m_domain; |
||||
QHostAddress m_address; |
||||
int m_interface, m_protocol; |
||||
quint16 m_port; |
||||
QHash<QString, QString> m_txt; |
||||
|
||||
void* m_data; |
||||
}; |
||||
|
||||
typedef enum { TCP, UDP } Protocol; |
||||
inline LIBQTAVAHISHARED_EXPORT QString serv_prot2str(QString service, Protocol prot) { |
||||
switch (prot) { |
||||
case TCP: return QString("_%1._tcp").arg(service); break; |
||||
case UDP: return QString("_%1._udp").arg(service); break; |
||||
default: return service; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif // QTAVAHI_P_H
|
@ -0,0 +1,91 @@
|
||||
#include "qtavahimodel.h" |
||||
|
||||
namespace Avahi { |
||||
namespace model { |
||||
Row::~Row() { } |
||||
|
||||
int Row::columnCount() { return 0; } /* columns of child rows */ |
||||
Row* Row::index(int /* row */) { return 0; } |
||||
int Row::rowCount() { return 0; } |
||||
|
||||
Row* RowList::index(int row) { |
||||
return list.value(row); |
||||
} |
||||
int RowList::rowCount() { |
||||
return list.length(); |
||||
} |
||||
|
||||
QVariant Root::data(int , int ) { return QVariant(); } |
||||
} |
||||
|
||||
|
||||
BrowseModel::BrowseModel(Avahi::model::Root *root, QObject *parent) |
||||
: QAbstractItemModel(parent), m_root(root) { } |
||||
|
||||
BrowseModel::Row *BrowseModel::getRow(const QModelIndex &index) const { |
||||
if (index.isValid()) { |
||||
return static_cast< Row* >(index.internalPointer()); |
||||
} else { |
||||
return m_root; |
||||
} |
||||
} |
||||
|
||||
int BrowseModel::columnCount(const QModelIndex & parent) const { |
||||
Row *row = getRow(parent); |
||||
return row ? row->columnCount() : 0; |
||||
} |
||||
QVariant BrowseModel::data(const QModelIndex & index, int role) const { |
||||
Row *row = getRow(index); |
||||
return row ? row->data(index.column(), role) : QVariant(); |
||||
} |
||||
QModelIndex BrowseModel::index(int row, int column, const QModelIndex & parent) const { |
||||
Row *r = getRow(parent); |
||||
if (!r) return QModelIndex(); /* invalid */ |
||||
Row *child = r->index(row); |
||||
if (!child) return QModelIndex(); /* invalid */ |
||||
return createIndex(row, column, (void*) child); |
||||
} |
||||
QModelIndex BrowseModel::parent(const QModelIndex & index) const { |
||||
Row *row = getRow(index); |
||||
if (!row) return QModelIndex(); /* invalid */ |
||||
Row *p = row->parent(); |
||||
if (!p || p == m_root) return QModelIndex(); |
||||
return createIndex(0, 0, (void*) p); |
||||
} |
||||
int BrowseModel::rowCount(const QModelIndex & parent) const { |
||||
Row *row = getRow(parent); |
||||
return row ? row->rowCount() : 0; |
||||
} |
||||
|
||||
Avahi::ServiceKey BrowseModel::key(const Avahi::Service &s) { |
||||
Avahi::ServiceKey k(s); |
||||
k.interface = k.protocol = -1; |
||||
return k; |
||||
} |
||||
|
||||
void BrowseModel::insert(const Avahi::Service &s, void *priv) { |
||||
Avahi::ServiceKey k = key(s); |
||||
if (m_services.contains(k)) { |
||||
EntryList *el = m_services.value(k); |
||||
beginInsertRows(createIndex(0, 0, (void*) el), 0, 0); |
||||
el->list.push_front(el->newEntry(s, priv)); |
||||
endInsertRows(); |
||||
} else { |
||||
EntryList *el = m_root->newList(k); |
||||
QMap<Avahi::ServiceKey, EntryList*> tmp(m_services); |
||||
tmp.insert(k, el); |
||||
int row = tmp.keys().indexOf(k); |
||||
beginInsertRows(QModelIndex(), row, row); |
||||
m_services = tmp; |
||||
m_root->list.insert(row, el); |
||||
endInsertRows(); |
||||
beginInsertRows(createIndex(0, 0, (void*) el), 0, 0); |
||||
el->list.push_front(el->newEntry(s, priv)); |
||||
endInsertRows(); |
||||
} |
||||
} |
||||
|
||||
void BrowseModel::remove(const Avahi::Service &) { |
||||
} |
||||
|
||||
} |
@ -0,0 +1,115 @@
|
||||
#ifndef QTAVAHIMODEL_H |
||||
#define QTAVAHIMODEL_H |
||||
|
||||
#include "qtavahi.h" |
||||
|
||||
#include <QAbstractItemModel> |
||||
|
||||
namespace Avahi { |
||||
class BrowseModel; |
||||
|
||||
namespace model { |
||||
class Row { |
||||
public: |
||||
Row(Row *parent) : m_parent(parent) { } |
||||
Row() : m_parent(0) { } |
||||
virtual ~Row(); |
||||
|
||||
virtual int columnCount(); /* columns of child rows */ |
||||
virtual QVariant data(int col, int role) = 0; |
||||
virtual Row* index(int row); |
||||
virtual int rowCount(); |
||||
|
||||
Row *parent() { return m_parent; } |
||||
|
||||
protected: |
||||
Row *m_parent; |
||||
}; |
||||
|
||||
class RowList : public Row { |
||||
public: |
||||
typedef QList< Row* > List; |
||||
|
||||
RowList(Row *parent) : Row(parent) { } |
||||
RowList() : Row() { } |
||||
|
||||
virtual int columnCount() = 0; /* columns of child rows */ |
||||
virtual Row* index(int row); |
||||
virtual int rowCount(); |
||||
|
||||
protected: |
||||
friend class Avahi::BrowseModel; |
||||
|
||||
List list; |
||||
}; |
||||
|
||||
class Entry : public Row { |
||||
public: |
||||
Entry(Row *parent, const Avahi::Service &s) : Row(parent), service(s) { } |
||||
|
||||
protected: |
||||
friend class Avahi::BrowseModel; |
||||
|
||||
Avahi::Service service; |
||||
}; |
||||
|
||||
class EntryList : public RowList { |
||||
public: |
||||
EntryList(Row *parent, const Avahi::ServiceKey &k) : RowList(parent), key(k) { } |
||||
|
||||
protected: |
||||
friend class Avahi::BrowseModel; |
||||
|
||||
virtual Entry* newEntry(const Avahi::Service &s, void *priv) = 0; |
||||
|
||||
Avahi::ServiceKey key; |
||||
}; |
||||
|
||||
class Root : public RowList { |
||||
public: |
||||
virtual QVariant data(int , int ); |
||||
|
||||
protected: |
||||
friend class Avahi::BrowseModel; |
||||
|
||||
virtual EntryList* newList(const Avahi::ServiceKey &k) = 0; |
||||
}; |
||||
} |
||||
|
||||
|
||||
class BrowseModel : public QAbstractItemModel { |
||||
Q_OBJECT |
||||
|
||||
protected: |
||||
typedef Avahi::model::Row Row; |
||||
typedef Avahi::model::RowList RowList; |
||||
typedef Avahi::model::Entry Entry; |
||||
typedef Avahi::model::EntryList EntryList; |
||||
|
||||
Avahi::model::Root *m_root; |
||||
|
||||
BrowseModel(Avahi::model::Root *root, QObject *parent = 0); |
||||
|
||||
Row *getRow(const QModelIndex &index) const; |
||||
|
||||
public: |
||||
int columnCount(const QModelIndex & parent) const; |
||||
QVariant data(const QModelIndex & index, int role) const; |
||||
QModelIndex index(int row, int column, const QModelIndex & parent) const; |
||||
QModelIndex parent(const QModelIndex & index) const; |
||||
int rowCount(const QModelIndex & parent) const; |
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const = 0; |
||||
|
||||
protected: |
||||
virtual Avahi::ServiceKey key(const Avahi:: |