Re: [GnomeMeeting-devel-list] Ideas and considerations for the addressbook code



�ic Bischoff a �it :
***** VIEWING (GENERIC)

We need to be able to present a book to the user. For that, the view
needs to have a list of contacts (no, a single contact definitely isn't
enough!). So we need a function call to get a list of contacts.

In this paragraph I will assume that you mean "the view needs a list of all contacts from the address book driver". If it is not what you mean, please ignore this paragraph...

Well, all contacts may be asking too much. I expect drivers to keep things sane.

The view needs to display, say, 10 contects at a time. No need to keep a list of all contacts in memory.

All we "need" is a function that gets _one_ contact from the driver. The view can then use this information to keep the list of the 10 contacts that it currently displays.

Blah. If you're gonna loop on a list of contacts anyway, then get the list at once.

Saying "I get only the 10 I need" is nice, but how do you tell the driver which they are ?

Especially a roster XMPP generally comes as a single roster push you ask for when connecting, then little update pushes.

The problem is that this list will change during time (new contacts
added, others removed, presence changed, status message changed, and the
list goes on). So the view will need to be notified about it. Hence the
idea that the book will have "contact-added" and "contact-removed"
signals, and the contacts a "updated" signal.
In addition to that, there is an interesting special case to handle :
you have a contact in a book, and this contact shows up in a search
result. When you remove the contact from the book, we want to make it
disappear from the list too. Hence a "removed" signal for contacts.

While this sounds cool, I am not sure that this will be possible in some circumstances. The underlying address book (evolution, KDE address book, LDAP server, Zeroconf, ...) might not have the mechanisms to send such a signal.

No problem : then they don't.

Let's take the avahi book. It will probably update the list of users regularly (and have some action to update *now*). How will it work ? Well, it will populate a cache with those contacts, and on updating, flush it ("contact-removed" emitted on all of them), then repopulate it ("contact-added").

I can imagine situations where some users on machine A update a LDAP server on machine B while Ekiga runs on machine C, with the network connection between B and C being temporarily broken. Will an update message come up to the driver, and if yes when and how?

If the backend doesn't give incremental changes, that still fits in my design.

What do I propose instead? To be less ambitious, and have an address book that only pulls the data from the data source. If the user does not press "refresh", then he/she might have outdated information. If he/she presses "refresh", then the addresses are fetched again from the source.

Bad. If the backend gives incremental changes, you don't handle it. Such a design works only for the most simple backends.

Now this is all nice, but what can we do if an addressbook contains
millions of entries ? And if a search gives thousands of results ? In
such cases, my stance is that the low level code should do something
about it, since it is the one giving the problem.

I do not like that repartition of tasks.

Let me take an example. When you write an operating system, you write:
- disk drivers
  (the low level code that does physical read and write from and to
   the hard disk)
- a filesystem
  (the high level code that gathers individual sectors into files,
   handles a cache, etc)

The low level code should not be bothered with handling the disk cache. Okay, the problem comes from the disk driver, because it does slow access to the medium. That does not mean that it's not another component, the filesystem, that can solve it.

Why this repartition of tasks? Because the performance problem is a problem that can be shared by many disk drivers. Therefore, the solution can be put in common in the high level code.

Your file manager doesn't handle the filesystems itself : they all present the same interface, and take care of caching themselves. I want the addressbook manager to be *high* level.

If some caching is needed between the addressbook manager and the backend, I want the driver to take care of it.

In fact, I implemented a SearchBook to take care of presenting all search results (whatever the book), and it's certainly desirable to provide a CachedBook, which will avoid code duplication.

How can the high level
code cope correctly with the situation anyway ? Only the low level code
can say "Eh, I already made two hundred contacts go through the wire for
that search, perhaps I should make it easy!".

Why transfer 200 contacts when the view knows that it needs only 10?

The view may show only ten at the same time on the screen, but the mouse wheel makes it so easy to go through all of the 200, that it wouldn't be wise to have them ready.

If there are several thousands, that's more annoying.

The question to really ask, and to which I have no satisfactory answer
is: "How do me make the low level tell the high level it had to cut the
list ?".

The views know that contacts 301 to 309 are displayed. It's the view that knows where to cut. In fact, there's nothing to cut, since all the view does is ask the driver for the contects it needs.

How do you number the contacts in a sane way?

***** VIEWING (CONTACTS)

The contacts should contain enough to be viewed from a generic view in
the contacts window, and be shown in a search result. That won't prevent
some other parts of the code to make a better use of them.

For example, in the contact window we will probably be able to see a
contact's title and description, but not its detailed presence, while
the main window will contain a much better and more specific view for
the live contacts.

You say that the "contact" class must have _enough_ information to be used both in situation A and in sitation B.

It would be better to have _different_ "contact" classes for situations "A" and "B".

Of course there will be. I was discussing the base class.

The list of contacts could just manage a name, a company and a phone number.

I would put the following :
1) a title (generally the name)
2) a description (for example a location, a status message, etc)
3) an icon

The search result could handle much more detailed information.

Of course it cannot. Your search result is a book which needs to be able to show avahi contacts, sip contacts, call history contacts, etc... so it can only show the contacts as generic.

In other words, there's no need to put all the model's data into the views' data. Each view can handle the data it needs.

The view which is specific to an addressbook, of course can show the data from the specific contact associated to it. But the generic view can only show generic information.

***** CONTROLLING (GENERIC)

Now, we want to be able to act on our objects ; say rename a book or a
contact, subscribe/unsubscribe to its presence, etc.

The best thing to do is to be able to get menus for each of them. Notice
the plural: we will certainly to have at least menu in the menu bar and
a popup menu.

The first bad news about such menus is that they won't be generic : they
will depend heavily on the specific type of object (no rename for avahi
contacts, subscribe/unsubscribe only for XMPP contacts...). So we will
certainly need some sort of shared actions that will be basic bricks for
building menu (like the shared-actions.[ch] of my first round of
proposals), that each implementation will mix as it sees fit.

I assume here that an 'implementation" is one of the address book drivers, and that you mean that the drivers will be in charge of building menus. If those assumptions are wrong, the paragraph below does not apply, please just ignore it.

Yes, that's precisely that.

Just the same as a hard disk driver does not display menus to the user, I think that the address book drivers in Ekiga should not be responsible for user interaction

The menu is supposed to contain actions which are specific to the given contact, book or view. Hence it must be created by code which is specific to that contact, book or view. It cannot be done by the generic code.

The high level code should be responsible for that. Of course the contents of the various menus depends on the address book capabilities. That's why the API between the driver and the high level code should include "query capabilities functions:"

	class addressBook
	{
		virtual bool isReadOnly() = 0;
		virtual bool canSubscribeContacts() = 0;
		...
	};

No, that is wrong and I already explained why. Putting everything in the base class is bad.

The second bad news about them is that they won't be static either. They
will have to update themselves following the object they are attached
to. Say your nice XMPP contact is connected with a client which has VoIP
support, but now connects with a no-VoIP client, and disconnects the
first : we want the "Call" menuitem to disappear to reflect this.

So my proposition (no code yet) is that those menus will be obtained by
subclassing of GtkMenu, register to get signals from the contact/book
they are attached to, and use that to keep themselves up to date.

In your example, a signal is not needed.

Knowing that there is no VoIP number in the current contact is enough.

That is wrong. The same contact could update itself and gain it. Or update itself and lose it.

***** CONTROLLING (CONTACTS)

Let's try to be more specific:
in avahi-contact-menu.h, we would have:
GtkWidget *avahi_contact_menu_new (AvahiContact *contact);
and in gm-contact.h:
GtkWidget *gm_contact_get_menu_new (GmContact *contact);

This later function would be different in each implementation, which
allows for example the avahi_contact_get_menu_new implementation to call
avahi_contact_menu_new. The high level code called a generic function,
but got access to a very specific implementation, which knows the full
api of the specific contact, and hence will provide the best service.

Again, this logic should be in the high-level code and needs not be avahi-specific. What exactly should be presented to the user could be determined from querying the address book for its capabilities.

Which means :
1) *all* of the addressbooks would by default have *all* the possible apis for such capability by default, with a isAbleToDoFoo function for each Foo capability ; 2) if an addressbook needs a new capability, we'll have to modify the base addressbook to add it in for it ; it won't just be a matter for the new code to add only what is necessary for itself in its own files.

***** CONTROLLING (BOOKS)

Perhaps the menu shouldn't be attached to the book himself, but to its
view. That would allow to place things such as "Show offline contacts"
in it. Mostly the idea would be to have controllers for both the
model-as-model and its view-as-model.

Agree.

Nice.

Congratulations, comments and flames welcome, but flames will end in
/dev/null.

It definitly deserves congratulations. You forgot to mention one of your nice ideas, that it that it should be possible to search in several address books at a time.

Well, that is the main side-effect of having a base class for contacts.

For the comments, please note that my comments are based on a lot of assumptions and guesses. In particular, I assume that you have in mind a different assignment of tasks of the various software layers than the one that I would like to see. If those assumptions reveal wrong, then of course my comments do not make much sense.

I think you are focused on an addressbook as something both very big and very static, which explains many of our divergences.

If I did not misinterpret your thoughts, you have a very lazy view of the high-level code. It's software that sits down and waits for signals to act. Somehow, the high level code provides "services" to the drivers. The drivers are the ones who understand what's going on.

Yes, that's it. The high-level code doesn't know what an AvahiContact or EDSContact is ; it only knows avbout GmContact. That means it cannot do fancy things with them, and has to call functions on them to do anything interesting.

I have the opposite view: the address book drivers are simple and stupid. Their main task is to go and fetch the requested contacts. The high level code has an active attitude: it queries the drivers for contacts, it queries the drivers capabilities, it displays the contact windows, it handles the menues... The high level code has the real intelligence.

Your high level code is very complex, and has to be modified heavily anytime we add a new type of addressbook with new capabilities.

Anytime you want to do something, you should ask yourself the question: "Who knows how to do it?". You want to send a message to a contact ? Who knows if it's even possible, and how to do it ? The contact itself! Hence this should be handled by the contact.

Of course, as-is that would mean a lot of code duplication. That's where we can use a toolbox ; of which you had an example in the first round of sample code I sent in private.

To make it explicit for the -devel- readers, this sample code had a function:
void shared_action_rename (GObject *obj);
so any object which could be renamed, had only to:
- have a "name" property ;
- provide an action which trigerred that function on itself.
the function handled showing a nice dialog to rename the function.

The idea is that of course quite a few objects will have common capabilities ; but instead of putting them all in the high level code and clutter it with functions and conditional, I want to provide the bricks which the low-level code will use to build a specific and well-fit system.

Snark



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]