Monday, 23 March 2009

Notifications

For the last few days I've been splitting the big Kadu god-class. It has so much code, that it's enough to make five other classes (Core, KaduWindow, KaduWindowActions, StatusMenu, StatusButton) and I think I could split it even more! Core (with Core::instance()) manages starting/stopping the application and setting/getting the current status. KaduWindow only implements gui of the main window... It is now closer to one-class one-purpose principle.

The next task is to move notify and notify_window code from modules to core. There are many places in kadu_core that could use notify capabilities and I don't see any reason to forbid that (and it will allow us to remove the MessageBox class).

I think our notify system is quite powerful, it supports many back-ends (like showing windows, playing sound, showing hints...) and it allows placing actions inside notifications. The only thing it is lacking is showing a value, e.g. a progress bar for file transfers. If we add it, it could be possible to integrate our notify system with KDE or GNOME notifications.

A little bit about our system: it consists of two base classes, i.e. Notification (for a messages being displayed to the user) and Notifier (for backends), and the NotificationManager class (for managing the two former).

Notification

If you want to create a message for the user, you should use the Notification class (or one of its descenders). Each notification has a name (e.g.: "newChat", "incomingFile", "connectionError") that identifies an event (notifications are configurable by name). Other properties are: title (used in WindowNotifier), text (content displayed to user, e.g. "New message from XYZ"), details (less important data, e.g. content of message sent to us), list of contacts and of course an icon.

Many notifications depend on the currently used account (e.g. "connectionError"). For those notifications the AccountNotification class should be used. Of course you could extend any of these classes to add new information, but not every notifier will be aware of them.

The Notification class has a concept of 'callbacks' - it's a list of actions that a user can perform (e.g.: 'accept' and 'ignore' on the 'incomingFile' notification). The only way to create a notification with a callback is to create a new class with slots for each action and call addCallback(tr("Action description"), SLOT(...)). You can also call setDefaultCallback(int timeout, SLOT(...)) so the user won't have to decide by herself or himself. If you would want to receive only the default callbacks 'accepted' and 'discard' (e.g. generated by left and right-clicking on hint), then just overwrite the callbackAccept and callbackDiscard virtual methods.

Also, notifications can live for some time. Every notifier can 'acquire' notification, so it won't be deleted until every 'acquirement' is canceled with 'release'. Sound notifiers do not 'acquire', but window and hint do. As long as the Notification is alive, it can be changed, but as I can see now, no signal is emitted. I consider it a flaw (look how the hints module appends new messages to hint...).

The last thing to know about notifications is that they need to be registered (so users can edit settings for each of them individually). It is done by NotificationManager::registerEvent and you can unregister it (for example if your module gets unloaded) by NotificationManager::unregisterEvent.

Notifiers

For implementing a new notifier, you should create descendant from the Notifier class with an overridden method notify(Notification *) (and register it with NotificationManager::registerNotifier). If your notifier will support callbacks, re-implement CallbackCapacity callbackCapacity() too (and return CallbackSupported there). You will also need to use notification->acquire() if it will display/update the notification content/icon anywhere. Of course after the notification has hidden use notification->release() to free memory.

If notifier can be configured, you should also re-implement NotifierConfigurationWidget *createConfigurationWidget(QWidget *) - it should return a new instance of the new class descending from NotifierConfigurationWidget. The most important function to re-implement is switchToEvent(const QString &). It should save the state of the currently edited event and display the state of the new one - given its method arguments.

In the end, saveNotifyConfiguration should store all changes made by the user in the configuration file.

Proof-read by Stiep

Wednesday, 18 March 2009

GSOC updates

Google rejected us ;(
How sad. So we will have to do some of our projects without their support.
Maybe next year... When we will have XMPP support and Kadu will be more recognizable in the world ;)

Status updates

No big changes recently in Kadu code (lack of time as usuall), but there are some things I would like to share:
  • we've got new developer - Juzef (http://juzef.idl.pl/) got full read-write access to Kadu svn repository, congratulations ;)
  • we've applied for Google Summer Of Code 2009 (our submission is still not verified), there is list of our ideas: http://www.kadu.net/w/GSOC2009 - of course anyone can start working on it, even if Google does not want us ;(
  • big Kadu class will be splited into KaduWindow, Core, CoreActions and maybe some others; mixing GUI logic with main class of Kadu is not a good idea (something like God Object antipattern)
  • Chat concept appeared, it will allows to have Gadu-Gadu conferences on contact list and not only that ;)

Wednesday, 11 March 2009

Signals, slots and ignoring OOP principies

Refactoring of gadu_protocol module is now in cleanup mode. There are still some missing things, like DCC not supporting NAT connections, DCC configuration is not moved to Account settings page and so on...

I had some thoughts that I've got while I did the refactoring: about signals and slots overuse in gadu_protocol code. This Qt extension of C++ is supposed to help programmers with connecting events that happens on one object with actions that another object should perform when an event is triggered. The overuse occurs when the connection is always one-to-one and we know that no another object ever will try to connect to these slots or signals. Just like in gadu_protocol - classes like GaduProtocol, GaduProtocolSocketNotifiers, DccManager, DccSocketNotifiers were communicating with each other using this, but I think it is not the optimal solution. It needs connecting these objects (this is a lot of code to write!) and signals-slots invoking is really very slow (I know it is not a concern because of small frequency of these calls in protocol classes, but I still does not like this solution).

I've got into conclusion that these classes are really an one big class that is just split for convenience. So I've removed signal-slots where it was possible and made these classes each ones friends. They can now access private members of each other ;) In the result, it is easier now to understand how this whole group works together. We did not lost any of flexibility, because there was not any either (you cannot just extend a closed communication protocol like this).

This is not very good solution from view of OOP (no accessors, just friend classes), but it is the best one for my simple mind ;)

Monday, 2 March 2009

Sending files

Kadu trunk can now send and receive files. Dcc6 and Dcc7 are supported. NAT to NAT Dcc7 connections will not work (libgadu issue). I've not checked Dcc6 callback connections (public-ip to NAT). If anyone has Gadu Gadu 6.x and is behind NAT - please contact with me on Kadu forum, so we can test this too.

What is left? Notify-based questions (accept file/reject file) and merging new file transfers with old ones (like sending rest of the same file in another session).