Saturday, 15 September 2012

Expanding emoticons, URLs and other DOM manipulations

Introduction

Users of IM applications are used to be able to click links in conversations and see little smiley faces instead of strings like :) or :(. This all has to happen regardless that messages are passed (at least in Gadu-Gadu) only with bold/italics/underline formatting. So this is our job to find all links and emoticons in text and change them.

DOM processing using Visitor pattern

The best way do to that is to get through DOM tree of message (converting bold/italics/underline formatting to proper HTML DOM is not that hard) and do everything on DomText nodes. Unfortunately Qt does not give an easy way to do that - one  have to traverse DOM manually. So we have wrote DomVisitor and DomProcessor classes that allows us to manipulate DOM trees with ease. DomProcessor acts as acceptor of visitors for QDomDocument with possibility to add, remove or update nodes is DOM tree.

Implemented DomVisitor subclasses are:
  • IgnoreLinksDomVisitor - very important proxy visitor that disables processing of all content under a tags - we do not want to expand emoticons inside links after all (and :/ is very popular)
  • EmoticonExpander - expands emoticons into images
  • DomTextRegexpVisitors - expands all matches of regexp expressions in DomText nodes in whole document into list of nodes (that requires another subclass)
So all we need to do is to parse message into DomDocument, wrap it into DomProcessor and run every required DomVisitor.

Collapsing emoticons

Users also want to be able to copy content of message in plain-text format, with textual representation of emoticons. It would be nice to be able to run our inverted DomVisitor instances (like EmoticonCollapser) on text copied from QWebView. Unfortunately it is not possible, as many Adium styles use files that are not valid XML (just HTML). It is not possible to convert copied content to valid XML, so we have to use regular expression to extract emoticon and link content.

For some time we were considering using non-visible QWebView and manipulate QWebElement objects instead of QDomNode, but this seems a bit overkill. We also do not want to force our users to use only fully-XML compilant Adium styles to make copied text always valid XML... I would be very happy to hear about working Qt-based HTML to XML converter.

Code

Code is available at our Gitorious repository. Feel free to download it!

Wednesday, 29 August 2012

Merged Proxy Model

Kadu IM blog is now part of Qt Planet. This is good time to start sharing some of our solutions with community.

Merged Proxy Model

This is the thing I'm most proud of - a model that can merge any number of other models into one. In Kadu we use it in for:
  • display actions above/below items in QComboBox widgdets
  • display list of Chat and Buddy objects in our roster widget
The second usage forces us to use a complicated structure or models and proxy model, as we have one Chat instance for every Buddy in addition to other instances. So before merging these models we apply filter proxy models that removes one-buddy chats from it. Then we apply more filtering/sorting proxy models on merged model just to be nice for our users.

Features

This proxy model not support everything - for example we haven't tested how it works with multiple columns or submodels with different number of columns. It also does not allows to plug in a model under a subnode of other model - all models are on top level.

It supports following features:
  • updating list of submodels on demand
  • inserting and removing rows (no support for moving)
  • emiting all row and data based signals properly
  • proper mapping to source and from source indexes
  • copying mime daya from submodels
So it is pretty usefull already.

Implementation

I must say that is was very hard piece of work. MergedProxyModel has about 450 lines of code and we have KaduMergedProxyModel on top of it (to implement reverse mapping from values to multiple indexes).

Two basic concepts are used: boundaries list that stores begin and end row index of all submodels in merged model. This value is updated every time a row is inserted or removed. Second one is mapping that maps QModelIndex instances from source to proxy model.

Code

If you want more details or the code please look at our git repository: Kadu on Gitorious. This file is most important: merged-proxy-model.h. Feel free to download and change it. And please send us any update that will make this code better.

Thursday, 19 July 2012

Sending images in 1.0

Every good Gadu Gadu IM application supports sending images inside chat. Kadu supports it as well (unfortunately, not for XMPP/Jabber protocol). Until now it was implemented in a strange way that required inserting strange strings looking like [IMAGE /path/to/image] into chat window. Now we are WYSIWYG:

Sending images in WYSIWYG mode

There is also another improvement: receive images are replaced directly in chat window, without redrawing whole conversation. It saves some CPU cycles and removed ugly flickering when redrawing.

Wednesday, 18 July 2012

QML in Kadu 1.0

1.0

Yes, it is true. Next version of Kadu will be 1.0. After 10 years we have finally decided that Kadu is mature enough to get a full version ;)

It does not mean that it is now finished. It won't even have all the features that we would like to put in it (migration from other clients, voice-video chats, integration with telapthy and so on). It is impossible for such a small team to do it all in a reasonable amount of time.

But still, it will be great release with some new features and a much smaller bug count than 0.12.0. I'll present first one now.

QML

As some of you know, QML is a quite new language for UI design created by Trolltech for Qt. It is really nice JSON-like language that allows for creating animated and responsive UIs in fraction of time of other solutions (like QWidgets).

Its usage in Kadu is still very small and experimental, but we have first, very promising, results.

This is new look and feel of Open Chat With... window:

QML version of Open Chat With... window

It has really nice animations of selection and very cool kinetic scrolling (no scrollbar for now).

It will get better with Qt5 and QtQuick (QML) 2.0! Stay tuned ;)

Monday, 30 April 2012

XMPP roster problems

Updating roster data should be easy:
  1. update data
  2. send to roster
  3. wait for confirmation
But of course it is not. For example, Facebook reports 501 not implemented on each roster operation. We have added code in 0.12.0 to support this - when 501 result is received we don't try to update this roster entry anymore. Kadu just uses data (nickname and list of groups) entered by enduser. So this is not a problem anymore.

Google+ has the worst behavior possible. It accepts our changes, sending OK response. But after a second it sends roster entry update to client restoring previous data. This means if someone for some reason decides to join two Google+ contacts into one buddy in Kadu he/she will get nickname dance on this buddy - changing from one to another. Only way to get rid of it is to manually disable synchronizing of these contacts with remote roster.

Thankfully this is all possible in incoming Kadu 0.12 (already in beta1).

Tuesday, 17 April 2012

Composition > Inheritance

In some cases composition is much better than inheritance.

Some classes in Kadu must emit signals after each change of its properties. When change is made to a lot of properties at once only one signal should be emitted (for performance reasons). So three classes got separate implementations of this behavior. Even worse, one of these classes was parametrized (it was template class). Qt does not support signals and slots on parametrized classes, so a workaround was created by adding virtual method emitUpdated that was reimplemented in every subclass to emit proper signal.

It was a lot of work. And it was error-prone.

Then a new simple class was born: ChangeNotifier. It has only one job - notify about changes. It support emitting only one signal per bulk of changes. It was added as member to these three classes mentioned above. A lot of duplicated code was removed. Strange emitUpdated method was also removed - ChangeNotifier is not parametrizedm, so it can emit signals without problems. Other classes just connect to ChangeNotifier signal instead of one of original class. And this class can be easily unit-tested and reviewed.

Really great. Next time I'll think twice before creating inheritance.

Tuesday, 27 March 2012

0.12.0-alpha4 - MUC and movable history file

MUC support

Gadu-Gadu has conferences. XMPP/Jabber has MUC - Multi User Chat. MUC is much more like IRC - conversation is done in rooms which people can join and leave.

For now MUC support in Kadu is very basic - you can join and leave room (protected with password or not), see everybody in that room, chat with people and see chat's history. As MUC XEP is very, very, very long I've decided to not implement everything at once (it would take several years at least). We will just add features that people needs in next releases.

Join Room Window


Movable history file

Again, we have updated history file format. If you've used older alpha version of 0.12.0 you must remove kadu-0.12.conf.xml and history2.db from your Kadu profile directory.

New history format can be moved between installations of Kadu without problems. It does not use UUIDs anymore to connect database entries with contacts and chats. Thanks to that we were able to remove a lot of data from configuration file (it is now 30% smaller for me).

Saturday, 3 March 2012

0.12.0-alpha3 - new Plugins window and icon theme selector

I know it is two weeks since last alpha, but previous week was devoted to new stable release - 0.11.1. Get it as soon as you can ;)

Here is more about great new stuff incoming in 0.12.0!

Good looking plugins window

Out plugin window had GUI that remembered 0.5.0 or even 0.4.0 release of Kadu. It has non-intuitive interface (double-clicking for plugin activation/deactivation) and was just ugly. Fortunately, Juzef started to work on this. Here are screenshots of effects of his work:

New Plugins Window.

As many of you can tell it is directly inspired by KDE4 plugin dialogs. An by KDE4 source code (OpenSource is great for this kind of stuff). We are thinking about making this one part of configuration window as another section. What do you think about this idea?

Icon theme selector with preview

This one fixes a 5 years old feature request. I think that screenshot will be enough:

Icon Theme Selector with preview
New configuration file

Updated configuration format requires new configuration file. From now it is called kadu-0.12.0.conf.xml - so changes wont be visible between instances of Kadu 0.12.0-alphaX and Kadu 0.11.1. Be careful with that!

Bartosz has worked on Gadu-Gadu connection problems that some people are experiencing. These changes will probably go into Kadu 0.11.3 (that will be released in 3 weeks from now). He also found a way to make our Aspell bundle on Windows much smaller. Current installers have less that 20MB in total (compared to almost 40MB of 0.9.0 release).

Saturday, 18 February 2012

0.12.0-alpha2 - new Set Status window and more history improvements

Another week, another alpha release. This is what we call "release often, release early".

Status window

This release brings you an effort of hard Juzef's work - new Status window. It allows you to change status and description (or presence and presence message, as in XMPP) in one window with multi-line description editor and history of as many previous description as you wish.

Select your new status
Select one of previous descriptions

History improvements

As requested after last post, I've added F3 and Shift+F3 shortcuts for search in chat windows and history. Also search bars in history window are always visible (in every tab) to make them easier to use.

Another small feature is that history can be disabled per buddy and per chat.

Here you can disable history for given buddy...

... and given chat


History recovery

Since we are using Sqlite3 for history storage several users suffered from broken database (about three of them if I remember correctly). So I've added a history recovery feature to Kadu 0.12.0. Unfortunately, it only works on Linux when one have sqlite3 binary installed. But this is still better than our competitors features ;)

History recovery and import - all in one window

Friday, 10 February 2012

0.12.0-alpha1 - new History Window and Search Bar

Work on Kadu 0.12.0 has started when 0.11.0-rc1 was released. This was whole month ago, so it it best time for first testing release of next version ;)

History window

My personal goal for 0.12.0 is to improve history as much as possible. I started with fixing our History Window. To be honest, it is really horrible and unusable in 0.11.0. Try to find something there. Yeah, it is possible, but very, very hard.

So let me present you the 0.12.0's History Window. It is:
  • useable,
  • beautiful, well-aligned,
  • fully asynchronous,
  • with powerful search capabilities and built-in search bar
As usual, screenshots:

Chats view in History Window
Search view in History Window
As you can see I've decided to use tabs instead of tree view to separate all chats (simple chats and conferences combined now) from statuses and smses history. Search got its own tab. You can filter buddies and chats by name and ID as in roster widget in three first tabs and in comboboxes of search tab. You can search for given word in every chat (or only selected one) or in statues of every or selected buddy. All found occurrences will be highlighted in chat window and nice search bar allows to navigate through them. Use Enter and Ctrl+Enter in search bar to go to the next/previous found item.

This is not final design, it still needs some polishing. I'll be very happy to see input from you on this blog comments or on our forum.

Chat window

And new, special feature: search-bar inside each chat window (use Ctrl+F to show it).

In-chat search bar
It works in exactly the same way as in History window - press Ctrl+F, enter word to search and hit Enter (or Ctrl+Enter) to find first/next or last/previous occurrence in current chat window! There is no highlighting all occurrences in chat window for now, I don't really know if should be added here as well.

Also, you can use Ctrl+Home/End/PageUp/PageDown to scroll chat window without mouse.

Friday, 6 January 2012

0.11.0 - part four - visual improvements

During 0.11.0 cycle we have spent some time improving UI of Kadu. Here I'll present a short list of most visible changes.

Toolbar buttons with menus

In 0.10.0 menus for toolbar buttons were implemented in Kadu and it was not very pretty - there was no distinction between normal buttons and menu button.In 0.11.0 we use Qt functionality and there is a small arrow to indicate that menu is available.

Button with menu can have default action (that is executed when button is clicked) or not. First ones have a small arrow next to button, second ones have an arrow directly on button.

Menu button without default action

Menu button with default action
More tabs

Tabs now will shrink when you have a lot of open chats. This allows to show more of them without need to scroll.
Really lot tabs shown at once
Unfortunately, tabs icons will no longer blink. There is Qt bug that caused all tabs to scroll to left when icon was changed - reaching tabs that were far on the right was impossible.

Message notifications

You will no more miss a message - every unread one is now indicated on dock icon, roster, window icon and tab icon. In 0.10.0 only messages for non-open chats were shown. Also placement of icon on roster was improved - it is now shown instead of status icon, non next to it.
New message icons on roster
Add conference window

Creating a conference required a lot of work - one had to Ctrl+select all needed buddies and then right-click and select "Chat..." command. New window will make this easier - you can easily select buddies, name a conference (or not, if it is one-time only) and start it. Lets have a look:
Add conference window
In this window you will no longer lose any selection by accidental click. Filtering by name, group, id  makes it very easy to find required buddies. Visible name can be set for a conference - then it will be displayed on top of roster widget.