Archive for the 'GSoC' Category

GSoC wrapup – Konqueror new bookmarks system

So GSoC ended already, as most of you already know. I haven’t been blogging as much as I would like, and I didn’t achieve to finish on time everything I wanted, but that’s not a defeat, only a delay – I will continue working on this bookmarks system until it can get merged into trunk. And then I’ll fix incomintheg related bug reports =).

So the state of the art is: we’ve got an akonadi resource for konqueror bookmarks which stores the bookmarks in nepomuk. We’ve got a bookmarks organizer, and we’ve got a bookmarks menu integrated in konqueror. The new location bar is however not finished yet, but that will be fixed within days.

A lot has changed since last report. I’ll try to explain here what’s the base structure of the new classes and what I’ve been doing, mainly related to the location bar, and why I followed that design decisions.

KCompletion

The current konqueror location bar uses a KCompletion class for autocompletion, which is mainly handled by KLineEdit. But how does it work? Let’s use a simple example. Imagine you are developing kmail’s new email dialog. For the line in which the user enters the destination email address, you could use a KLineEdit, but in order to easy the task of the user, you could use autocompletion like this:


KLineEdit* destinationLineEdit = new KLineEdit(this);
...
KCompletion completion;
completion.addItem("tigerwoods@fortune.com");
completion.addItem("billgates@microsoft.com");
...
destinationLineEdit->setCompletionObject(completion);

You could of course get the list of items to add to the completion object from the address list too ;-). If you were developing instead the location bar for dolphin, you would like to have directories listed while the user is typing. Instead of adding and removing items manually yourself to the completion each time the user types, you can use a KUrlCompletion object that does that automatically.

But for konqueror location bar we have a problem: it needs to be able to use kurlcompletion for completing directories, but it also needs to complete from bookmarks and history. We need to work with multiple completion objects at once even though KLineEdit and similar classes can work only with one. Also, we need to do more complex completion A normal KCompletion object contains a list of strings and matches what you type with those, but for having an amazing location bar we need more power: if I type “work” and I have a bookmark tagged “work”, I want it to show up in the completion list even if its URL doesn’t contain the word “work” at all. And that’s only the beginning. I want to be able to set an order of the completed items depending on the relevance and type of the items, and more..

Enter Qt Model-views

I need to confess that I love Qt model-views classes. QAbstractItemModel, QAbstractItemView, QAbstractProxyModel, QSortFilterProxyModel, QTreeView.. They provide an standard convinient and flexible way to manage and display almost any kind of collections. Even collections of completed items. Actually, the completed items popup is in reality a QListWidget, which displays a model associated with the completion object..

But that is an internal model which KLineEdit doesn’t let me change. So the question that follows is.. why not use directly my own custom item models instead of a KCompletion object? And that’s what I did, even if it required a lot of work to be done.

First of all, I tried to outline in paper the master plan. What classes needed to be created for everything to work fine. Then I wrote step by step what needed to be done before what. Then I started following those steps one by one and it worked!

Places all over the.. place

The plan was that the location bar autocompletes places. A place could be:

  • a history entry from history entries model
  • a bookmark from bookmarks model
  • an url from a kurlcompletion model

I had already a bookmarks model and konqueror already has an history entries model, but I had no kurlcompletion model. I looked at the code of the KUrlCompletion class I decided that I didn’t want to rewrite it.. so I simply created a KCompletionModel which acts as a proxy and converts a KCompletion object in a QAbstractItemModel. So to create a url completion model, I do:

KUrlCompletion* urlCompletion = new KUrlCompletion();
KCompletionModel* urlCompletionModel = new KCompletionModel();
urlCompletionModel->setCompletion(urlCompletion);

To do a completion, I connect the urlCompletion to the textChanged(QString) signal from the lineedit. And the completion objects reflects the changes which in turn instantly appear in the model. It’s not the best solution but hey it works.

Also, another problem was that the completion object of KLineEdit would be replaced by a model, not three. But now as you see I have again multiple completion objects, so what was the solution? creating an aggregated model created out of multiple models. It works like this:


KAggregatedModel* aggregatedModel = new KAggregatedModel(this);
aggregatedModel->addSourceModel(bookmarksModel);
aggregatedModel->addSourceModel(historyEntriesModel);
aggregatedModel->addSourceModel(urlCompletionModel);

Now, those who know how an item model works will probably have a lot of questions about that. For example one could be.. Do all those models need to share the same columns? The answer is no. The aggregated model I created is quite simplistic in the way it works:

  • It assumes the source models have only one level of childrens.
  • It only shows one column, which is the default display column (sourceIndex.data(role)).
  • It shows the list of items as a list, showing first the items from the first item model, then the ones from the second, etc.

Places Manager

So far we have an aggregated model with completed urls, unfiltered bookmarks and unfiltered history entries. That’s yet not complete from being the amazing completer model. What we need to do is the amazing filtering and sorting. That’s done by the final completion model, the master of places..

It’s called PlacesProxyModel and inherits QSortFilterProxyModel. It takes a QAbstractIteModel and takes the Konqueror::PlaceUrlRole for each index, gets the URL, and obtains the corresponding Konqueror::Place for it. Then, knowing already all the available information for that place, tries to match it against the query the user entered in the lineedit and sets its relevance for sorting purposes. It also filters out duplicated entries. Quite an achievement, but how does all that work again?

First off, all previous models (url completion, bookmarks and history entries models) report their items to a Places Manager which keeps track of them and contains a Place for each relevant url. So for example, if the user has bookmark for http://www.kde.org and it’s been visited yesterday, there’s a place in the places manager with the information from both the bookmark and the history entry.

All those models also support obtaining the url related to each item by using the Konqueror::PlaceUrlRole. The aggregated model proxies calls to retrieve data from any role, including that. So in the end the information can be retrieved by the places proxy model. Then an algorithm that takes into account the number of visits, the last visit, if the user-written string matched the place’s url, title or tags, etc sets the relevance of the items in the proxy model.

The road to the location bar

The new location bar inherits from the modified KLineEdit which uses a QAbstractItemModel for completion, which I have named KLineEditView. An amazing location bar needs to be able to show an star that can be clicked to add/remove a bookmark, and the autocompletion needs to show for each place shown if it’s bookmarked, its tags, etc. Contextual information.

State of the art

What has been done? All the previous things I have mentioned are working. They can always improve, but the code is already there. The location bar widget is the last thing I started to write so it’s not finished: it has already plugins support so that new icons/sub-widgets can be shown inside the location bar. Now I need to write the plugin to let the user bookmark the current location, and I also need to write the CompletionPlaceDelegate to have a properly eye-candied completion list. And I’ll do it shortly.

Future and end

Unfortunately, a lot of things need to be done before this new bookmarks system ends up in konqueror trunk. Revamping such an integral part of Konqi is not a simple task. We also want to be sure that when the replacements comes into play the user doesn’t have to suffer it but to enjoy it instead, so I need to find fix and all the regressions. I need to write documentation and test cases too. I honestly don’t really know when the job will be done but I know I’ll continue working on it.

I want to thank specially my mentor David Faure for the support, for giving me a thumbs up even if I didn’t finish on time everything I wrote in my gsoc propossal. You rock david!

This is post has been too large I admit, so I think I’ll stop here :D. If you read everything yay you’ve got too much free time so go and do something more useful!

Anuncios

GSoC: I Am Alive! (And bookmarks too!)

First I want to apologise because I haven’t blogged fo a month about my gsoc. But that doesn’t mean that I haven’t been working, quite contrary. I’ve been having limited connectivity and the limited time I got access to Internet I didn’t feel like blogging. Now I do. Lot’s of things have happened. I’ve even suffered the H1N1 but don’t worry about that because it’s not as bad as TV shows: for me it was two days with flu and the third day I was 100% ok.

The konqueror bookmarks menu is already rewritten to support the new bookmarks system.  I like to read see other people’s code because it can inspire me when I want to develop something. Thus, I’ve been reading Arora code and I liked their idea of using QAbstractItemModel as the data source for QMenu and that’s how I’ve implemented the new bookmarks menu.

I’ve always wanted to have menus more advanced than just normal menus. When I saw that inside Mac OS X Help menu there’s a search bar I knew I wanted to have something like that in KDE and I wondered if that was possible. The answer is yes. Qt Menu system allows to insert custom widgets with WidgetAction class so I’ve added a Search Bar to Bookmarks Menu. When you type in the search bar, the source model of the Bookmark menu changes to a DescendantsPRoxyModel which represent all bookmarks and folders in a flat list and uses a QSortFilterProxyModel to filter out results. It’s not finished yet, there are some issues with the search but all in all I’m quite satisfied. Next step: Awesome bar, and fix more bugs =).

I’ve also been working on the bookmarks organizer, fixing bugs, adding support inline bookmark editing in the bookmarks view using double-click, adding a BreadCrumb view similar to Dolphin’s breadcrumb mode but using a QAbstractItemModel as a source for paths, etc. Here’s a small videocast where I show some of the mentioned features in action:

GSoC biweekly report++ – State of the bookmarks editor

Summers is already here for those who haven’t noticed – we’re having 44ºC here in Sevilla during the day and yesterday it was 36ºC at 23:30. That’s how Spain can be like in summer, so GCDS assistants you’ve been warned and don’t forget swim-wear!

First week of June I went to the AC/DC Madrid concert and it was awesome, but I didn’t program much that week. This week I’ve been working in the bookmarks editor, and now you can see that it’s getting more featureful. However I’d first like to share some comments about other bookmarks editors I’ve found over the net.

Safari 4 bookmarks editor with the iTunes-like cover view the most impressive:

Safari4b1CoverFlow

I haven’t tested it, but it’s quite clear that the cover view eats a lot of space. They remove the space dedicated for showing the details of current bookmark, and instead (I guess) the bookmarks are editable “inline”.

Another interesting point worth mentioning is that in the left side they have two sections: Collections and Bookmarks. In the new Konqueror bookmark editor I’m using Akonadi, and Akonadi already uses the concept of collections internally so something like that will be easy to implement. Instead of showing in the left a Tree with a Root element and everything inside that (Bookmarks Toolbar, Bookmarks Menu, Recently Added Bookmarks, Unclassified bookmarks, etc) I could use a lateral panel similar to the one used in the Open file dialog with those items, and then show a breadcrumb widget over the bookmarks listing widget to let the user know (and manage) the current location:

bookmarks 15 june - open file

Okey now let’s see what I’ve done so far. You can add, remove bookmarks, and edit them (either using the line edits or even inline in the bookmarks view), and also I’ve borrowed some code from dolphin to make the bookmarks view columns resize nicely. And you can show whatever columns you choose, it’s up to you:

bookmarks 14 june

If you look closely, you see that the menus are pretty similar to the current bookmarks editor menu. I’ve just replaced the Bookmarks and Folder menus with an Organize menu. Taking a look at Firefox bookmarks organizer vs. current Konqueror bookmarks editor, I see that Konqueror has more options but even then they seem quite intuitive. Also something to wonder is how is FF bookmarks organizer missing toolbar buttons for the most common actions: New Bookmark, New Folder, Remove.

bookmarks 14 june - cols

Next week, I want to have all the already listed features in the bookmarks editor working (see for example the “Find in bookmarks”? it doesn’t work. Same for the breadcrumb which is just some text in a label, etc), and the following week I’ll hopefully have sorted out how to do the virtual folders structure and show it to the user in the bookmarks editor.

GSoC Weekly report 3-4: The Akonadi-Nepomuk connection

First of all I apologize because last week I was busy doing school stuff and I didn’t get to do much GSoC related stuff. I’m glad to be able to say that today I will assist to my last university class in this academic year and that my holidays (of code :P) period has thus virtual started, because I have also already done my last exam last week, yay.

BTW,  I am going to Akademy too! I already can’t wait to get there 😀

I’ve been investigating how well can I make Akonadi and Nepomuk play together. It seems that my decision of only using Nepomuk through Akonadi is possible. I will thus have an usual Akonadi Resource which provides a set of collections with their items (bookmarks) and subcollections (bookmark folders), and then also I will have some virtual collections.

A virtual collection is a new concept in Akonadi, which still is not enterely developed and Konqueror will probably the second (after Akonadiconsole) application taking advantage of it. A virtual collection has all the properties of a collection, but the items it contains are peculiar. They are not items as in a normal collection, but virtual items, which link to real items inside real collections.

Using virtual collections, besides having your usual bookmarks menu, you will also get in konqueror some virtual collections like: Recently added bookmarks, Most visited bookmarks, Recent tags, or <please suggest your virtual collection in a comment please!>.

You might then wonder… okey so Recently Added Bookmarks would be a virtual collection which contains urls which have been bookmarked recently. But how can an Akonadi virtual collection know which items comply with that? Only Nepomuk knows the relevant information. Here comes the Akonadi-Nepomuk connection:  you can create an Akonadi Virtual Collection with SearchCreatJob class, which takes two arguments: the title of the collection (Unclassified Bookmarks for example) and the SPARQL query string for Nepomuk.

Using SearchCreateJob, I just need to write a SPARQL query that returns only the recently added bookmarks, which is quite trivial. Then Akonadi will handle that query to a Nepomuk persistant query, and it will tell akonadi whenever resources are added or removed to that query. In turn, Akonadi will get the Uri of those added/removed resources, and use that as the remoteId of the Items in the virtual collection, and add/remove those items accordingly. Autotically if there the item with that very same remoteId in a real collection, Akonadi will know, and the item in the virtual collection will have the same properties: same payload, same name, etc.

Usecase to understand and recapitulate:

  1. You click on the star appearing in the location box to bookmark current url.
  2. The url gets added to some Konqueror bookmarks collection (if it’s an unclassified bookmark, it will added to a special-purpose Akonadi bookmark collection called “Unclassified bookmarks”).So now your bookmark has its corresponding real Akonadi item.
  3. Akonadi bookmarks use Nepomuk as storage, so the bookmark is also added in Nepomuk database. In fact it’s only stored persistently there, Akonadi only caches it.
  4. When the new bookmark is added to nepomuk, automatically nepomuk detects that the bookmark enters in the persistant query for “Recently added bookmarks” and adds the new bookmark resource to it, notifying to Akonadi server which was looking for changes in that persistant query.
  5. Akonadi server gets the notification of a new resource added to the “Recently added bookmarks” Nepomuk query,  and adds a new item to its “Recently Added Bookmarks” virtual collection. That new item will actually refer to the item added in step 1, with the same name, payload (a KonqBookmark instance), etc.
  6. Konqueror automagically gets the new bookmark in “Recently added bookmarks” subcollection!

To some people all this might seems like a lot of steps, but to me it sounds like angels. Because all this will make konqueror feel faster and it will make its bookmarking system responsive (non-blocking!) and improve it’s architecture. I am already thinking about how easy will it be to create kross plugins for bookmarks, to be able to support natively new types of bookmarks, to be able to sync with del.ici.us-style services, and even to easily make all this a bit more flexible and let other apps (Konsole, Kate, etc) use this new Bookmarks System. But, one step at a time :D.

GSoC weekly report: week 2

This week I’ll present you part of the technologies used in the brand new Konqueror bookmarks system. I’ll explaining how they work and how I’m going to use them, thereby showing part of the design of the new bookmarks model at the same time. Finally, I’ll also show you what’s the current development state and what I plan to do for next week.

Nepomuk

Nepomuk is where I will store Konqueror bookmarks. It’s very easy to do so. In my code, I only need the following code to create a bookmark in Nepomuk and put it in a bookmark folder:


Nepomuk::Bookmark kdeBookmark("http://www.kde.org");
kdeBookmark.addTitle("KDE main site");
Nepomuk::BookmarkFolder rootFolder("konqbookmarks:/");
rootFolder.addContainsBookmark(kdeBookmark);

That’s it! This line of code will create the bookmark in the Nepomuk database if it wasn’t already there, set the title of the bookmark, and it will add the bookmark to the Konqueror’s root bookmark folder. After executing that code, if I can search in Krunner for kde.org, and the Nepomuk runner will find the bookmark. And any application who wishes to list the bookmarks in the root bookmark folder can do so with the following bit of code:


Nepomuk::BookmarkFolder rootFolder("konqbookmarks:/");
QList bookmarks = folder.containsBookmarks();

UHm..what if I wanted to remove a bookmark from Nepomuk? I can do so every easily too by just calling to bookmark.remove();. It also allows me to attach a lot more information related to a bookmark of course. All kinds of resources and properties can be attached to a Nepomuk bookmark: tag, creation time, whatever. All in all, Nepomuk stand for an easy to use semantic database.

Akonadi

Akonadi is a convenient way to access data stored elsewhere. It isn’t designed to store the data itself, because it’s a middle layer between the application and the data. It gives power to the develop by providing an abstraction layer in which all the data is represented by collections and items in a tree similar to the concept of dirs and files in a filesystem.

A Collection is made up of other subcollections and items. And of course, there is a root collection to rule them all, master of the puppets. To clasify the data, both collections and items have one or more mimetypes attached to them. For example, a collection has the mimetype Collection::mimetype(), but it can also have the mimetype “application/x-vnd.kde.konqbookmark” which is the one I’m using right now for Konqueror bookmarks.

Now a bit more talk about the items. You guessed it, the items can have some data attached to them, but how? Using payloads. A payload is a supported Akonadi class that stores some data. It could be an email, or in my case, a bookmark. I don’t use directly Nepomuk::Bookmark (but I could if I wanted), but a wrapper class that I called KonqBookmark.

When one application request the KonqBookmark payload of a bookmark item, that data needs to go from the akonadi server process to the application process. This is done serializing the data in the server and deserializing it in the other side. As KonqBookmark is not a natively supported class by akonadi, I have to give Akonadi a class that serializes it, and then it works.

An application connected to Akonadi can add new items to a Collection, modify them, delete them. Same with collections: they can be addd/modified/removed. Imagine I’ve got two instances running of my bookmarks editor. If I remove in instance 1 a boomark item via Akonadi, Akonadi itself updates instance 2 of the bookmark editor and shows there that item is gone. Without me needing to do anything! No need to sync via d-bus, etc. It’s like magic, but one that really works.

So far, we’ve got Collections and items in the Akonadi server.. but how? where do they come from? They come from Akonadi agents, concretely from an Akonadi resource. An Akonadi resource provides a set of collections and their items. I’ve created the Konqueror bookmarks resource that adds some collections of bookmarks to Akonadi. But you could add for example a new bookmarks resource different from mine using the same mimetype as me, and provide access to a del.ici.us collections. You just need to code for the resource the part in which you access to the del.ici.us bookmarks and create the correspondent collections and items in Akonadi.

Akonadi is a middle layer, it doesn’t really stores the data. Thus, the bookmarks resource I’m developing stores the data in Nepomuk, as I explained earlier. If you create a del.ici.us resource as I said in the previous paragraph, you could access your del.ici.us bookmarks but they wouldn’t be the same as those stored in nepomuk. A new kind of akonadi agent comes to the rescue: Akonadi feeders. You could create a del.ici.us feeder instead for konqueror bookmarks, and then when a new Konqueror bookmark is added, it would also be feeded to del.ici.us, etc.

And what if I’ve added a bookmark when I was offline? what happens there? Akonadi then caches the bookmark, and it would be added to del.ici.us when you connect to Internet. This is transparent to the del.ici.us feeder: it only needs to have a function to add bookmarks to del.ici.us, and Akonadi will simply call it when it’s appropiate. Again, it’s like magic.

Current state

So far, I’ve got an Akonadi resource which can list bookmarks in the Nepomuk database, add new bookmarks, modify them, remove them. The bookmark items use a KonqBookmark class payload, which is supported by Akonadi as payload because I’ve also implemented a KonqBookmark serializer/deserializer.

I’ve created a sample application using this resource, a bookmarks editor, which you can see in the screenshot below (I promised you would get one this time ;-). This bookmarks organizer shows you the bookmark folders and bookmarks as usual, using two views: a tree of bookmark folders and a listing of bookmarks.

First screenshot of the very simple Bookmarks Editor

The application itself is very simple, because it uses model-view & delegate classes for displaying and editing bookmarks and bookmark folders. Some of these classes are provided by Akonadi (the model for Bookmarks collection for example), and I developed the other classes, like the model and delegate for Konqueror bookmarks.

I’m developing all this in the most reusable way so that other web browser that wish to share with Konqueror the bookmarks system can do it very easily. I remind all those curious people that the code is available in github.

Next steps

Next week, I plan to improve both the editor and the resource. I also want to start playing with Akonadi virtual collections for doing searches in the bookmarks too, and maybe I start doing some tests with Kcompletion stuff for the new location bar. Have a nice day!

GSoC weekly report

I have decided to do the GSoC weekly report thing. Sometime after deciding that I noticed that I need to actually write and post in the blog the report because the report doesn’t write itself – go figure! Technology doesn’t seem to be so advanced to do it automatically just yet. So here I go.

In this project I will be reimplementing the Konqueror bookmarks system using Akonadi and Nepomuk. I already have KDE trunk compiled but I needed to be sure both Akonadi and Nepomuk are up and running correctly. Both worked fine in my Samsung laptop setup, but in my Netbook I needed to install libmysqlclient15-dev deb package, rebuild qt-copy with -plugin-sql-mysql config option, and of course rebuild Akonadi, because even if I had other DBs working, the only one currectly supported in Akonadi is Mysql.

Oh yeah you read it right, I do compile and develop in an Acer Aspire One netbook, with an Intel Atom and 1Gb of RAM, and it works just fine. It’s a bit slower when rebuilding a whole package, but other than that it’s ok. And when doing such a thing (rebuilding qt-copy for example) I can always use icecream and let the other machines at home handle the tough compiling stuff.

Alright, now I have a working environment. Next step? Get used to Akonadi and Nepomuk, learn how to use their APIs to make myself feel at home with them and thus be able to develop the concrete details about how to design the new Konqi bookmarks system. Thanks to the Akonadi and Nepomuk tutorials found in Techbase, the nice API documentation found in api.kde.org, the code itself of both libraries and the pieces of sofware which use those utilities (like krunner or dolphin), together with the invaluable help provided by friendly people in #akonadi and #nepomuk-kde channels, learning is a joy and it’s very easy to do so. That’s something to be proud of in the KDE community!

I’ve setup a public Github repository in which I will dump everything I’m doing related to my GSoC project. This way I will use it to synchronize easily my laptop and my netbook. That means this repo will really contain everything I’m working on. At the moments there’s nothing close to being useful for anyone else than me, it’s all tests and the tutorials of Techbase a bit modified for my needs. So children be aware this is not for public consumption, so you’ve been warned. On the other hand and once said that, it has the advantage that you can see what I’m doing right now if you are brave enough to do so.

This blog post is becoming larger than I pretend it to be, so I will stop here today. Next time I will talk about what’s Akonadi and what’s Nepomuk, how I’m going to design the architecture of the brand new bookmarks system and what is so cool about it. And I promise I will insert at least a picture in next gsoc weekly report even if that means choosing one randomly from 4chan.org/b or /dev/null (is there a difference anyway?). Have a nice day!