Thursday, 8 January 2009

Status and description

I had a GPU failure on my laptop, so I was unable to do any work for Kadu for 2 weeks. Now as the ULE to Contact porting is in progress, I'll present how status/description changes are done in Kadu internals.

The problem

Changing status seems to be an easy task - create an object with enum (Online, Busy, Invisible, Offline) and QString fields, modify values and pass it to protocol-handler object. But at some time people started writing plugins that were modifying statuses in many ways:
  • amarok module - could add information about currently played song to description (before or after user-supplied one)
  • filedesc module - was reading lines from file, one by one, and setting it as user description
  • autoway - change status to offline/invisible/busy after some time of inactivity, description could also be modified as in amarok
  • ...
User could load and use any status-changing modules at one time.

This is how amarok module worked:
  • when amarok started to play song, current user status was stored
  • user description was changed by adding song information
  • when song stopped, old (stored) status was restored
And autoaway:
  • when user was inactive for given period of time, current user status was stored
  • user description and status was changed
  • when user activated, user status was restored
If you have some experience with multithreaded programming, you'll see the possible problem right away. If user used amarok and autoaway modules and the steps were executed in following order:
  • (amarok) 1 - amarok stores "user status"
  • (amarok) 2 - amarok changes status to "amarok status"
  • (autoaway) 1 - autoaway stores "amarok status"
  • (autoaway) 2 - autoaway changes status to "autoaway status"
  • (amarok) 3 - amarok restores "user status
  • (autoaway) 3 - autoaway restores "amarok status"
The status of user will not be set to "user status" - so the behavior is plain wrong.

The resolution

To resolve this problem I've created two classes: StatusChanger and StatusChangerManager. These two operates on old UserStatus class (that will be replaced by new, much simpler, Status - but this is another story).

StatusChanger is abstract class with method virtual void changeStatus(UserStatus &status). This method can do anything with status - append, prepend something to description, replace some tags with any content, even change the status to Offline (which means logout from Gadu Gadu network).

StatusChangerManager allows for registration and unregistration of StatusChangers. StatusChangers are joined in chain, ordered by their priorities. The first StatusChanger in chain is always UserStatusChanger, which is controlled by user. When user changes status the UserStatusChanger::userStatusSet(UserStatus) is invoked, which emits signal statusChanged. This signal is received by StatusChangerManager, and every StatusChanger's changeStatus is invoked:

UserStatus status;
for (int i = 0; i < statusChangers.count(); i++)
 statusChangers.at(i)->changeStatus(status);

After that statusChanged(Userstatus) signal of StatusChangerManager is invoked, and resulting status is sent to server.

Every StatusChanger can emit statusChanged slot and trigger status rebuild in chain.

Resolved
Now amarok module is replaced by mediaplayer (much more general module). All status-changing modules uses new classes. There is no problem like before, because all modules provides only 'operations' that changes copy of user status (so it is always remembered). After removing or changing one of 'operations' status is rebuild starting from the one that user set.

SplitStatusChanger

Kadu has class SplitStatusChanger (in status_changer.cpp). It allows for setting statuses with description of any length for Gadu Gadu. If it is too long for protocol to handle it - it is splited in number of parts. Each part is displayed for specified period of time.

If you want to test it - please find place in code suitable for inserting it in chain, also remove the description limit from status dialog (look in misc.cpp).

No comments:

Post a Comment