Difference between revisions of "Development/Tutorials/Decibel/Handling TextChannels"

Jump to: navigation, search
(add .service file code)
(start explanation of handleChannel implementation)
Line 95: Line 95:
 
}
 
}
 
</code>
 
</code>
 +
This method is reimplemented from the <tt>Decibel::ChannelHandler</tt> interface. It is called when ever a new incoming communication channel (<tt>QtTapioca::Channel</tt>) comes into existence. In this method, we must decide whether to accept the incoming channel, and if so, to deal with it appropriately.
 +
 +
First we check the incoming channel and connection objects are not invalid (=0) and we fail if this is the case. In a proper application, these error cases should be handled more gracefully, but for the purpose of this demo, we will just use <tt>Q_ASSERT()</tt> to handle them.
 +
 +
Next we check if the <tt>m_connection</tt> member variable is set to 0. If it is not, then we are already handling a channel. This demo can only handle one channel at a time, so we return <tt>false</tt> to reject the new incoming channel.
 +
 +
There are multiple types of channel we could possible receive. In this demo we are only interested in the <tt>TextChannel</tt> type. So the next step is to try and cast it to a <tt>QtTapioca::TextChannel</tt> object. We then evaluate if this cast was successful (not = 0) and if it was not, we again return <tt>false</tt> to reject the channel.
  
 
====onCloseChannel()====
 
====onCloseChannel()====

Revision as of 06:57, 13 March 2008

Handling TextChannels
Tutorial Series   Decibel Tutorial
Previous   C++, Qt
What's Next  
Further Reading   CMake

Contents

Abstract

This tutorial will walk you through the process of creating a simple application that uses Decibel's TextChannels to communicate via arbitrary instant messaging networks using telepathy. From the result of this tutorial, only a few more lines of code are needed to produce a functioning text based instant messaging client.

This tutorial is based on the simpleclient demo included with Decibel. An explanation of using the simpleclient demo can be found here.

In this tutorial, we only pick out the important parts of the source code to discuss. The complete working source code for this example can be found here in KDE's SVN Repository.

ChannelHandler class

The first class we will need to create is an implementation of the Decibel::ChannelHandler interface.

The Class Definition

Here is the class definition for our implementation of the Decibel::ChannelHandler interface. It is explained below.

class MyTextChannelHandler : public Decibel::ChannelHandler {

   Q_OBJECT

public:

   explicit MyTextChannelHandler(QObject * parent = 0);
   ~MyTextChannelHandler();
   bool handleChannel(QtTapioca::Connection *, QtTapioca::Channel *, const bool);

public slots:

   void onMessageReceived();
   void onCloseChannel();

private:

   QtTapioca::Connection *  m_connection;
   QtTapioca::TextChannel * m_channel;

};

We must inherit from Decibel::ChannelHandler which is the interface definition for handling incoming Channels. We reimplement the constructor, Destructor and the handleChannel() method as well as adding two slots of our own. We also create the member variables m_connecion and m_channel which will hold the QtTapioca::Connection and QtTapioca::Channel objects that are received by the handleChannel method.

The Class Implementation

Now that we have defined our Decibel::ChannelHandler subclass, we need to implement its methods.

Constructor

MyTextChannelHandler::MyTextChannelHandler(QObject * parent) :

   ChannelHandler(parent),
   m_connection(0),
   m_channel(0)

{ } The constructor is very simple. We just pass the parent object on to the parent class and initialise our member variables to 0.

Destructor

MyTextChannelHandler::~MyTextChannelHandler() { } The destructor is even simpler. We don't need to do anything in it because Qt will handle the deletion of child objects automatically.

handleChannel()

bool MyTextChannelHandler::handleChannel(QtTapioca::Connection * connection,

                                        QtTapioca::Channel * channel,
                                        const bool)

{

   Q_ASSERT(connection != 0);
   Q_ASSERT(channel != 0);
   if (m_connection != 0) { return false; }
   m_channel = dynamic_cast<QtTapioca::TextChannel*>(channel);
   if (m_channel == 0)
   {
       return false;
   }
   m_connection = connection;
   connect(m_channel, SIGNAL(messageReceived(const QtTapioca::TextChannel *, const QtTapioca::TextChannel::Message &)),
           this, SLOT(onMessageReceived()));
   connect(m_channel, SIGNAL(closed()), this, SLOT(onCloseChannel()));
   onMessageReceived();
   return true;

} This method is reimplemented from the Decibel::ChannelHandler interface. It is called when ever a new incoming communication channel (QtTapioca::Channel) comes into existence. In this method, we must decide whether to accept the incoming channel, and if so, to deal with it appropriately.

First we check the incoming channel and connection objects are not invalid (=0) and we fail if this is the case. In a proper application, these error cases should be handled more gracefully, but for the purpose of this demo, we will just use Q_ASSERT() to handle them.

Next we check if the m_connection member variable is set to 0. If it is not, then we are already handling a channel. This demo can only handle one channel at a time, so we return false to reject the new incoming channel.

There are multiple types of channel we could possible receive. In this demo we are only interested in the TextChannel type. So the next step is to try and cast it to a QtTapioca::TextChannel object. We then evaluate if this cast was successful (not = 0) and if it was not, we again return false to reject the channel.

onCloseChannel()

void MyTextChannelHandler::onCloseChannel() {

   m_connection = 0;
   m_channel = 0;

}

onMessageReceived()

void MyTextChannelHandler::onMessageReceived() {

   QList<QtTapioca::TextChannel::Message> message_list = m_channel->pendingMessages();
   for (QList<QtTapioca::TextChannel::Message>::const_iterator
            message = message_list.constBegin();
        message != message_list.constEnd(); ++message)
   {
       if (message->type() == QtTapioca::TextChannel::Message::Normal &&
           message->contents() == QString("ping?"))
       { m_channel->sendMessage(QString("pong!")); }
       m_channel->acknowledge(*message);
   }

}

The main() function

int main(int argc, char ** argv) {

   QCoreApplication app(argc, argv);
   app.setOrganizationName(Decibel::organisation_name);
   app.setApplicationName("SimpleClient");
   MyTextChannelHandler thandler(&app);
   Decibel::registerTypes();
   QDBusConnection::sessionBus().registerService(Decibel::organisation_name + '.' + "SimpleClient");
   QDBusConnection::sessionBus().registerObject("/TextChannelHandler", thandler);
   return app.exec();

}

The .component File

[Component] Name=Simpleclient Protocols= Types=0 Targets= Service%20Name=org.kde.SimpleClient Object%20Path=/TextChannelHandler

The .service File

[D-BUS Service] Name=org.kde.SimpleClient Exec=@DEMO_INSTALL_DIR@/decibel_simpleclient_demo

CMakeLists.txt

SET(QT_DONT_USE_QTGUI "YES") INCLUDE(${QT_USE_FILE})

SET(SERVICE_FILE "org.kde.SimpleClient.service")

INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include

                   ${CMAKE_BINARY_DIR}/src/client
                   ${CMAKE_SOURCE_DIR}/src/client
                   ${QT_QTCORE_INCLUDE_DIR}
                   ${TELEPATHY_QT_INCLUDE_DIR}
                   ${TAPIOCA_QT_INCLUDE_DIR}
                   ${CMAKE_CURRENT_BINARY_DIR}
                   ${CMAKE_CURRENT_SOURCE_DIR}

)

CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/${SERVICE_FILE}.cmake"

              "${CMAKE_CURRENT_BINARY_DIR}/${SERVICE_FILE}"

)

  1. ######### simpleclient demo #########

SET(simpleclient_SRCS

   simpleclient.cpp
   mytextchannelhandler.cpp

)

SET(simpleclient_MOC_HDRS

   mytextchannelhandler.h

)

QT4_WRAP_CPP(simpleclient_MOC_SRCS ${simpleclient_MOC_HDRS})

ADD_EXECUTABLE(decibel_simpleclient_demo ${simpleclient_SRCS}

              ${simpleclient_MOC_SRCS}

) TARGET_LINK_LIBRARIES(decibel_simpleclient_demo

                     ${QT_LIBRARIES}
                     ${TAPIOCA_QT_LIBRARIES}
                     decibel

)

INSTALL(TARGETS decibel_simpleclient_demo DESTINATION ${DEMO_INSTALL_DIR}) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SERVICE_FILE}

       DESTINATION ${DBUS_SERVICES_INSTALL_DIR}

) INSTALL(FILES org.kde.SimpleClient.TextChannel.component

       DESTINATION ${COMPONENT_SEARCH_DIR}

)

Running our Application

TODO


KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V.Legal