Hi All,

This email is to explain a bit the design and structure of aMSN2.. so it's
leaning more towards developers than towards users...
As I explained in my previous mail, the design is simle :
1 - three layers : protocol, core, gui
2 - the protocol is taken care of by pymsn
3 - the gui is abstracted and any front end can be used (multi-front end
client)
4 - the programming language is python...

So for the technical details, first, download the source code. I've now
imported it into our public SVN, and you can get it from the SVN URL :
https://amsn.svn.sourceforge.net/svnroot/amsn/trunk/amsn2
You will see the three layers in the amsn2 subdir, we have :
gui/
core/
protocol/

The idea is that the protocol layer will be the glue between pymsn and the
core, it will receive events from the core and notify pymsn, and it will
receive events from pymsn and notify the core.
This allows for one thing : modularity! I do not want to have code mixed
everywhere again like we had with aMSN 0.x... This would also 'in theory'
allow for switching to a different protocol or to a different library, BUT
we do NOT want to do this. It was decided that aMSN is an MSN clone and if
you want a multi protocol client, there are many good ones out there.. we
want to concentrate on the MSN protocol so users can have a fully featured
client, we want all our efforts to go into MSN compatibility. End of the
discussion!

The GUI layer takes care of all our GUI needs, we have a 'manager' that
basically stores which front ends we have installed and allows us to access
their classes... a front end can register itself from anywhere by calling
the guiManager.registerFrontEnd, and giving it its module
(sys.modules[__name__] ).
Alen has maybe a different idea about this and we'll see what he comes up
with, so this design can change a little...
NOTE that everything so far is in proof of concept mode and anything can
change at any time!
The way I see the front ends is a bit like a set of mega widgets... or an
'abstracted toolkit API' but higher level...
I will talk again about front ends once I explain the core a bit...

The core.. the most important thing out there.. that's the heart of aMSN2,
we want every front end to act the same, to have the same features, and this
will be accomplished thanks to the core! The core is the glue between the
protocol and the UI, it will take care of profiles, configuration, options,
non-protocol/ui features, etc...
The most important part of the Core in terms of the other layers is the
'views' concept. This design will allow us to have a really nice client,
easy to build new front ends, and a consistent feature set amongst all the
front ends...
What is a View? Well it's simple, it's how we want the user to 'view' our
UI. For example, the GUI doesn't need to know if a contact is online, away
or busy.. the UI doesn't need to know a contact's nickname or psm, or a
group's "count/total".... the UI only needs to know *what* to show the
user... If we tell the UI to show a specific icon (depending on the state)
for a contact, as well as what 'name' to give the contact (including the
PSM.. or not, depending on the user's options)... and if we tell the UI that
the group's name is "X (count/total)".. that's all it really needs to
know...right ?
well, that's what the views are all about, they tell the UI how to display
an object...

So there's this magical thing that I called 'StringView'... it's a 'view' on
a 'string'... basically, it's a list of elements specifying how the string
should be displayed... A StringView can be something like this :
list : color red text "my nickname" image <img> text "<-- that was a smiley"
color grey text "(away)" color red text "\n" italic true text "psm" italic
false
(the stringview can also contains tags...)

With a StringView we can represent anything we want.. the nickname of a
user, including smileys, including status, including the psm, etc..
The reason why we do it like this is because :
1 - we don't want every front end to have code for parsing the smileys
(what about custom smileys)
2 - we don't want every front to need to access the user's preferences like
"show psm beside the nickname"/"show psm under nickname"/"don't show the
psm"
3 - the UI doesn't need to know the status/whatever of a user, it just needs
to know *how* to display it.. because that's what the user will see in the
end...


So.. now that you know about the StingView, let's talk about ContactView..
as you can see every ContactView has a unique identifier representing the
contact.. an 'icon', which will be an Image that gets built by the core
(using the front end Image class API).. it can be a buddy icon depending on
the status or the display picture (depends on the user's options), it can
also contain 'emblems' like 'blocked', or 'away' emblem on top of the
display picture, or a 'alarm' emblem if the user has one...
We do not want the front end to need to look through the options, or to
query the core for whether a contact has an alarm or not, or query
the protocol to see if the contact is blocked or not....
The ContactView also has a 'name'.. that's a StringView to represent the
contact.. it will contain the nickname (or custom nickname), parsed smileys,
the psm (depends on option)... the color of the nickname will depend on the
status of the contact, or depends if the user sets a 'custom color' on the
user.. etc... You now understand that we don't want every UI to start
checking if the user has set a custom nickname for the user, or if there's a
global custom nickname (or psm), or if he has a custom color for the user,
etc...
The ContactView also contains a 'dp' attribute which is an Image with the
display picture of the user... the reason why this is different from the
'icon' attribute is because we might want to show them both.. for example :
[icon]  nickname   [ display ]
          psm           [ picture ]

Like pidgin does for example... whether that attribute contains the DP or
nothing will depend on the user's options again...
Finally, we have a 'menu' which is the contextual menu when you right click
on the user (it's a MenuView item describing what the menu contains) and a
'tooltip' which is a TooltipView on what to show in the tooltip...

Now, I'll expain what the GroupView class is... it's a view on a group..
hehe.. it's the same thing again, the unique id, the icon (will change from
collapsed to expanded icon), the name (StringView containing the name of
the group + the count/total), menu and tooltip.. but we have one more item :
'contacts'.. it's an ordered list of the contacts contained inside
that group...

The other view classes are pretty much self explanatory...
Now, we move the front end.. the front end is given to the core as a module
object, the module contains all the classes we want/need and the core
creates them as he needs...
Any front end should implement the amsn2.gui.base.* classes, we have a few
classes in there, it's not that complicated :
mainloop : this is just an abstraction over the main loop.... pymsn needs
the glib mainloop (hopefully removed soon), but you might want to run the
qt/gtk/efl main loop, depending on what toolkit you use.. you implement this
here...
main : the main window, this window will contain the login screen or contact
list... the login window and contact list windows will need to use this
window to show themselves inside of it.. why.. because we don't want to have
a window appear/disappear when you start logging in...
image : simple class to abstract an image.. use whatever library you want to
represent an image.. (QImage? gdkpixbug... etc...)
login : this is the login window... the base classe has documentation on
each function.. it should be straight forward... this implementation is not
yet finished, as we need a way to feed the login window the list of profiles
as well as the list of "sign in as " statuses for each profile (if a profile
has a saved custom state)....
contact_list : this is the interesting one... the base classes functions are
documented so it should be simple but I still want to explain this a bit :
When you look at the api, you will notice something, there are only 4
functions :
contactUpdated, groupAdded, groupRemoved, groupUpdated...
You might be asking "humm.. why isn't there a contactAdded, contactRemoved,
etc...", well, it's simple... here's how it would work, on initial connect,
you get a bunch of calls to groupAdded with the groups of your contact list,
in each of those groups, you have the contacts (remember,
GroupView.contacts), so the front end gets this, and displays the group with
its contacts...
in every possible scenario of the contact list getting modified, the 4
functions are enough to make the UI sync with the protocol...
nickname changed : groupUpdated + contactUpdated ( the groupUpdated is
called if the sorting is done by nickname, so the front end gets the new
ordered list of contacts and it reorders the contacts in the UI)
psm changed : contactUpdated
status changed : groupUpdated + contactUpdated (the groupUpdated because the
sorting can be done on  status, or if the user goes offline (if we group
offline contacts together, then we get groupUpdated for both groups) the
'name' (count/total) of the group might also change, and contactUpdated
because the '(status)' or color might change)
new user : groupUpdated (the list of contacts has one more, the UI adds it
when it synchronizes the UI list with the groupview.contacts list)
deleted user : groupUpdated (same as above)
contact blocked, unblocked, etc... whatever : contactUpdated
sorting changed (ascending/descending/alphabetically, etc..) : groupUpdated

etc...
What this means is that the sorting is of course left to the Core.. the core
decides on how to sort the contacts, etc.. it's not the UI's job.. but it
also means that the UI must keep on each group widget, the ordered list of
contacts that are shown with their unique id, so it can resynchronize itself
when it gets a groupUpdated... of course, you can just delete all contacts
from the UI and rebuild them.. but try to be optimal in terms of performance
please... thanks...
This also means that.. I know, you won't like it.. but! it is necessary...
when a user clicks on a group, you must NOT collapse/expand it.. you must
tell the core about that event and let the core decide... sending you a
groupUpdated() with an empty list of contacts in the GroupView...
The reason is simple :
1 - the GroupView gives you the 'icon' for the group.. and that's what's
deciding if the icon is expanded/collapsed...
2 - imagine an option 'never collapse groups'... we don't want a UI that
ignores that option...
3 - imagine the option on a contact "always show this contact"... so we
would want a 'collapsed' group.. but still showing a contact...
4 - what about 'collapse/expand' on double-click only...

to be able to do all that, we need the UI to work this way! I know that for
some toolkits it will be easy/intuitive but for others (like gtk with its
treeview) it will actually involve more work... sorry about that!

Also please note that *for now*, we don't have all that working... humm..
yeah.. there's not much done with the icons.. the protocol doesn't do much..
there is no "group_clicked" or anything like that yet.. so be patient and
we'll try to make the core evolve as fast as possible...

any comments are appreciated!

Thanks for reading!
KaKaRoTo
-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Amsn-devel mailing list
Amsn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/amsn-devel

Reply via email to