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

No comments:

Post a Comment