Maintainership/2

I’ve just committed my development trunk of Dictionary on the CVS, as usual to new-dictionary branch.

Features

  • a navigation menu, with shortcuts for first/previous/next/last definitions
  • a Ctrl+Shift+G shortcut for search backwards
  • a “Save a Copy” menu item, used to save the content of the query to a text file;

It also features a stub for the user’s manual and some clean-ups for the build system.

What’s next?

I’m working on the printing system and the preferences dialog.

I’ve decided to remove the font preference, in order to make it the same of the desktop; I don’t want yet another font chooser. The only place where it makes sense to have another font is when printing – and for that we might just have a key in GConf.

Also, the text highlighting is going to be scaled down: since we are going to use multiple dictionary sources, over multiple languages, we really don’t know a priori some things, like how the enumerations will look like. The only text modifiers I’m going to keep will be [...] attributions (which will be rendered using italics); \...\ phonetics (which will be rendered in a styled color, default: gray); {...} links (which will be rendered in a styled color, default: blue).

[]
[]
[]

Maintainership

Vincent kindly asked me to join the maintainer team of gnome-utils, and I more than gladly accepted.

In order to celebrate the event, here’s the latest screenshot of my development trunk for Dictionary:

gnome dictionary new
the new Dictionary

As you can see, the current state is not far from the actual, stable, Dictionary:

gnome dictionary old
the old Dictionary

What’s missing:

  • the text parsing
  • the menu callbacks
  • the preferences dialog
  • the user’s manual
  • print support

The feature equivalence could require a bit more time than foreseen – especially the printing part and the manual – but I plan to have everything in place befor Christmas: think of it as my gift.

I plan to commit the new-dictionary to HEAD as soon as I have finished the menu callbacks and the preferences dialog.

[]
[]

Dictionary Applet/8

I’ve finally been able to begin working on the new GNOME Dictionary widgets. While GdictEntry still has issues – related to the speed of word look up and the entry completion – and has already hit CVS, in the last two days I’ve been hacking on the definition display widget, or GdictDefbox (I’ve retained the name from the old code).

The new Defbox code is a little bit more simple – things like links are not implemented (yet, even though I found them questionable), and the output is not quite as good looking as the old one; here’s the obligatory screenshot:

gdict-defbox

the new GdictDefbox

Aside from code cleanliness and the new multiple back-end system, what differences there are with the old Defbox? First of all, this is a composite widget and not just a TextView-derived widget; this allows neat things like the next feature, that is the embedded search pane:

gdict-defbox

the embedded search pane

It should Just Work®: Ctrl+F to view it, Ctrl+G for the next match, Esc to close it. Say goodbye to silly find dialogs.

gdict-defbox

the embedded search pane at work

The new GdictDefbox also allows jumping from one definition to another, by tracking them internally.

With GdictDefbox nailed down, a simple Dictionary application and applet might already be created; I plan to improve the definition box’s output and put it on par with the current one (plus enabling theming of the colors/sizes using GtkStyle) before actually coding the new Dictionary – but keep checking out the new-dictionary branch of gnome-utils in the next week or so.

Update 20051129@13:49: the spiffy new GdictDefbox is CVS as of some minutes. Go and try it using the gnome-dictionary/libgdict/test-defbox.c test case.

[]
[]
[]

Dictionary Applet/7

New week, new adventures

Actually, the number of new adventures has been strictly limited by my birthday first, and by a strange feeling of drowsiness that lasted all week (and still lasts as I’m writing this). Probably, it’s due to the fact that I’ve been sleeping like four hours per night since the last week-end. Yep, that’s probably it.

Dictionary Sources

The stuff (which should land in CVS as soon as I can fix up a test unit for it) coded in two days is the spiffy new dictionary source configuration file. Since we could have multiple dictionary sources, and thus multiple contextes, we also need a way to tell Dictionary what to use. Steal^H^H^H^HTaking inspiration from the new Gedit plugin systems which uses .desktop files for defining meta-data bound to a plug-in (author, name, description, etc.), I’ve designed a .desktop file for dictionary sources:

[Dictionary]
_Name=Default
_Description=Default Dictionary server
Transport=dictd
Hostname=dict.org
Port=2628

The Transport does the trick: it specifies which GdictContext implementation should be used. Each dictionary source file is interpreted by the GdictSource class, which loads the .desktop file from an absolute path and creates the right GdictContext for you.

These sources should go inside default directories – right now, the only hard-coded directory is $DATADIR/gdict-1.0/sources, but GNOME Dictionary will also check into $HOME/.gnome2/gdict-1.0/sources (and, in the end, I’d like to use $XDG_DATA_DIRS/gdict-1.0/sources). All the dictionary source files found in those directory will be loaded by the GdictSourceLoader object; using this object, you’ll be able to access the whole sources list or directly get the GdictSource for a particular name. Oh, and if you look at the format of the dictionary source file, you’ll see that both the Name and Description keys are localizable.

As soon as I begin adding transports (in form of GdictContext implementations) to GNOME Dictionary, more dictionary sources will be made available; if I add run-time plug-ins to libgdict in the future (maybe the next development cycle), those plug-ins will have to provide a dictionary source file in order to be used.

How does this change the code for a dictionary client? Now, you’ll have to load the dictionary sources and get the source you want, say the default one:

  GMainLoop *main_loop;
  GdictSourceLoader *loader;
  GdictSource *default;
  GdictContext *context;

  main_loop = g_main_loop_new (NULL, FALSE);

  loader = gdict_source_loader_new ();
  source = gdict_source_loader_get_source (loader, "Default");

  context = gdict_source_get_context (source);
  g_signal_connect (context, "definition-found",
                    G_CALLBACK (definition_found_cb), NULL);

  gdict_context_define_word (context, "vera", "GNOME", NULL);

  g_main_loop_run (main_loop);

  g_object_unref (context);
  g_object_unref (source);
  g_object_unref (loader);

As you see – nothing more complex that adding those lines.

Update 20051124@1435: I’ve committed my development trunk to the new-dictionary branch of gnome-utils. The src directory doesn’t build yet, since there’s not much to build, but everything works in libgdict, where all the fun is ATM. Now that we have most of the infrastructure in place, the UI should soon follow. I’ll make smaller check-ins, from now on.

Update 20051124@2126: I’ve coded in 10 minutes (while commuting from my university to home) and committed simple test suite for GdictSource and GdictSourceLoader (it works only if you install the dictionary source file – but it should give you an idea on how it works); I’ve also fixed a couple of dumb bugs (did I’ve already said that I’m a sloppy coder?) discovered when coding the test suite. While I’m waiting for Marta, I’ll begin porting GdictDefbox.

[]
[]

Dictionary Applet/6

The new GNOME Dictionary code has landed in CVS this morning! woot!

I hope I didn’t fuck everything up – as usual when I’m dealing with CVS. viewcvs seems to be fine – and the tree seems to build.

ATM, you won’t find much – just the context stuff and the dictionary protocol client implementation (with a test program); see the TODO for what comes next.

[]
[]
[]

Dictionary Applet/5

Don’t you hate it when you arrive near deployment stage and you have to get back to the design table because someone makes you notice that the architecture you’ve very cleverly layed out has one fatal flaw?

Well, to be honest, I hate it and yet I love it. Because it clearly puts your ego down and moves everything under a new perspective.

Let’s take GNOME Dictionary. Last week-end I was getting the UI down with the new GdictContext object; then, Reinout van Schouwen showed me bug #167366, and this week – aside from doing the (hopefully) last profiling session of BookmarkFile – I had to design a new architecture, flexible enough to be expanded with new back-ends.

Luckily, when I re-designed the whole implementation of the dictionary protocol client, I had it clearly separated from the whole code-base; this led to a session of “find-and-rename” and some minor code tweaking.

Now, the code in libgdict is a little more complex – but it’s expandible and should allow the creation of multiple back-ends more easily.

At top level, we still have GdictContext, but instead of being an GObject it’s now a GTypeInterface; every dictionary context must inherit it’s methods (and a locality property):

  gboolean (*get_databases)  (GdictContext  *context,
  			      GError       **error);
  gboolean (*get_strategies) (GdictContext  *context,
  			      GError       **error);
  gboolean (*match_word)     (GdictContext  *context,
  			      const gchar   *database,
  			      const gchar   *strategy,
  			      const gchar   *word,
  			      GError       **error);
  gboolean (*define_word)    (GdictContext  *context,
  			      const gchar   *database,
  			      const gchar   *word,
  			      GError       **error);

Also, we have the same signals as we had before the re-design:

  void (*database_found)   (GdictContext    *context,
  			    GdictDatabase   *database);
  void (*strategy_found)   (GdictContext    *context,
  			    GdictStrategy   *strategy);
  void (*match_found)      (GdictContext    *context,
  			    GdictMatch      *match);
  void (*definition_found) (GdictContext    *context,
  			    GdictDefinition *definition);

The first implementation of this interface is the GdictClientContext object, which is the dictionary protocol client object; in order to write a dictionary client, all you have to do is instantiate a new GdictContext implementation and use one of the GdictContext methods:

  GdictContext *context;
  GError *definition_error = NULL;

...

  /* we use a GdictClientContext */
  context = gdict_client_context_new ("dict.org", 2628);
  g_signal_connect (context, "definition-found",
     G_CALLBACK (on_definition_found), NULL);

  gdict_context_define_word (context, "penguin", &definition_error);

...

Adding contextes is a matter of writing a GdictContext implementation.

Note: Even adding run-time modules would be quite simple; right now, I don’t intend to – but nothing prevets you from doing it (hint! hint!)

Now that the architecture is a bit more solid, I’m going to land it into CVS. I’ll create a branch of gnome-utils for it; my code will live in a new directory, and will slowly move gnome-utils/gdictsrc out of build. Now the timeframe for my work has shifted a bit, but I’m still very confident on having a feature-equivalent version of GNOME Dictionary out before the end of December.

[]
[]
[]

Dictionary Applet/4

In order to let others know how’s the status of GNOME Dictionary Breaking is progressing (and in order to force myself working on it regularly ;-)), I’m sending the

Not So Weekly Status Report on GNOME Dictionary

Containing the status of the review-slash-breaking-slash-rewrite process of the GNOME Dictionary application and applet. Okay, it won’t be weekly, and I could simply use my blog instead of email; nevertheless, having someone to report to makes room for public discussion, and since I’m breaking (mostly) working stuff better be sure that some discussion actually happens at all.

So, what have you been doing so far?

First of all, I tried to salvage the ugly mess that was implementing the dictionary protocol (gnome-utils/gdictsrc/dict.[ch]). For the lack of better (or non 12-years-old-sister safe) words, I won’t use the ones I came up while auditing the said code. Suffice is to say that the code couldn’t be salvaged without a massive rewrite process anyway, so I scrapped out the implementation, and created a new, GObject-based one. You can see a preliminary version of the API usage on my weblog. How’s the rewrite going? I’ve pretty much implemented all the needed client commands for a RFC2229-compliant client, testing it both on dict.org and on my local dictionary server. The MATCH command is still missing, but I evaluate that will be a matter of 15 minutes worth of coding, in the new infrastructure. About the new infrastructure: I’m pretty proud of GdictContext; for example, now the client will advertise itself (using a predefined string or a custom one), and all the commands get queued; thus, you can set up a batch of commands (e.g. connect-lookup databases-lookup strategies-disconnect) without having to actually *wait* inside the callback for a command to complete its run. This will allow lazy data loading, and close one of my pet peeves about the current GNOME Dictionary, that is the absence of a CLI output; I’d like to write into a terminal:

$ gnome-dictionary --lookup "GNOME" --no-window

and have my definition of “GNOME” with no fuss (yes, I know that there already is “dict”, but what’s the point of having a dictionary client in GNOME if I also have to install dict?).

Another point of the new infrastructure is that lives all inside a shared library; which means that we can now have the much needed separation between the back-end code and the UI and the application/applet. Also, this means we can provide language bindings for it, allowing other applications to interface to dictionary servers (and I specifically thinking about the Deskbar Applet, here, even though I think there already is a dictionary protocol implementation in Python, using one based only on platform stuff would shorten the dependency chain).

What are you planning to do next?

First of all, finishing the client implementation (also, authentication could be added, if I can come up with a UI for it); adding a way to get some data like server’s capabilities, caching of the databases and strategies lists, etc. Also, I’ll do some code polishing and consolidation. This shouldn’t take long – probably no more than a week.

The UI is what comes next. The GdictEntry will be the first to be checked; I’d like to use the SexyIconEntry by Christian Hammond as a base for this widget, in order to have something more appealing (for the applet, mostly)- but first I’ve got to check how that behaves with GtkEntryCompletion.

The next widget to be targeted will be GdictSpeller; I’m still dubious about the form of this widget, or if it’s useful at all.

Finally, GdictDefbox. The main widget is pretty much right as it is, so I’ll just update it in order to use the new GdictContext; I’ll move the “find” dialog into a bottom pane a la Firefox, and I think that will be the most relevant change, UI-wise.

This would complete the road-map for the GNOME Dictionary Library (libgdict): the application and the applet would simply fall into place, once this has been sorted out.

When do we see the code?

This is the worst part of all. Since I’m basically rewriting everything, up until I can actually provide a working version of GNOME Dictionary I’d like not to taint the CVS repository; but I also understand the need for looking at the code, and comment on it – so, as soon as I’m able to create a full text client using libgdict, I’ll upload a tarball of it on my web space. If it passes a review from the maintainer, I’ll begin by creating a branch of gnome-utils HEAD with the new code, and keep working on it.

[]
[]
[]
[]

Dictionary Applet/3

In case you’ve been wondering if I had disappeared or something…

I’m in the middle of a session of exams, this week (four of them, to be precise), and I’ve had to cut down hacking time; nevertheless, I’ve been able to commit a set of fixes for the RecentChooser/BookmarkFile code and wrap up a new version of the desktop-bookmark storage spec.

GNOME Dictionary

I’ve also started the rewriting of the low-level implementation of the dictionary protocol client object for the GNOME Dictionary application. I’ve tried one last time to salvage the sane bits of the old code, but it was practically useless. Instead, I designed a new, object-oriented, approach. It’s been three years since I’ve done some networking with C, and I had to do some catch-up in order to write some decent code.

The new client object is called (with a certain lack of creativity) GDictContext; you create a new context by using the gdict_context_new() function, and since it inherits from GObject, all you have to do to destroy it is to call g_object_unref() on it.

All the querying and replying is done asynchronously; but instead of having callback functions for each command, I preferred a more GTK-like approach; now, you’ll have to connect to signals, which are emitted by the context object when something comes along the wire; thus, creating a client for the dictionary protocol is just a matter of a few lines of code:

static void
on_connect (GDictContext *context
            gpointer      user_data)
{
  GError *error = NULL;

  gdict_context_define (context, NULL, "penguin", &error);
  if (error)
    {
      g_warning ("Error: %s", error->message);
      g_error_free (error);

      g_main_loop_quit (main_loop);
    }
}

static void
on_def_found (GDictContext    *context,
              GDictDefinition *definition,
	      gpointer         user_data)
{
  g_print ("Definition for '%s' (from: %s)\n%s\n",
           gdict_definition_get_word (definition),
	   gdict_definition_get_from (definition),
	   gdict_definition_get_definition (definition));
}

...

  GDictContext *context;

  main_loop = g_main_loop_new (NULL, FALSE);

  context = gdict_context_new ("dict.org", NULL);
  g_signal_connect (context, "connect",
                    G_CALLBACK (on_connect), NULL);
  g_signal_connect (context, "definition-found",
                    G_CALLBACK (on_def_found), NULL);

  gdict_context_connect (context, &connect_error);
  if (connect_error)
    {
      g_warning ("Error: %s", connect_error->message);
      g_error_free (connect_error);
      g_object_unref (G_OBJECT (context));

      return -1;
    }

  g_main_loop_run (main_loop);

  g_object_unref (G_OBJECT (context));

  return 0;

As you can see, no more silly creation of commands and contextes, using redundant, badly defined or over-designed API (which, by the way, badly leaks strings and objects): just lean and mean, OO, event-driven code; I plan to make this a shared library (it already is, albeit it’s not exported), so every public function comes with documentation and follows the same style rules of the other platform libraries; I could even add bindings to it, and write the whole application/applet in an high-level language, like Perl or Python, and the effort would really be minimal.

Right now, the code is still flaky on the response parsing; I plan to make it more robust as soon as the week ends, and I’m free to use more time on it. After that, I’ll attack the UI side – and get a hold on the messy situation of the definition box, the speller tab and the preferences window. This should really take less than the low-level stuff: the code is a lot saner, even though requiring much love. The self-imposed deadline of late November/early December should be fully respected.

Dictionary Applet/2

As I said on the gnome-utils mailing list, the GNOME Dictionary codebase sucks.

Well, it sucks to the very end of it.

It really shows its age (it’s more than 5 years old), and suffers of what I’m used to call design by accretion. This particular technique of software design works like a black hole: particles are attracted to the singularity, and form the “accretion disk”, which is nothing but a big mass of… er… mass, graviting around, attracting other mass, and accelerating – thus getting hotter and hotter – until it eventually hits the event horizon. In software, it works the same way; new code is added, and it becomes relevant up to the point of becoming critical. The code becomes a little bit ugly, and it attracts other ugly code in order to make things work and to add new features. Time passes, and the codebase becomes a uglier mess at each iteration. At one point, everything simply collapses, because one of the “oh-so-hackish” portions of code simply passes the point of being understandable by any human being – including the original writer. Also, the overall entropy of the software increases, because no-one is smart enough to understand the code, let alone sanitize it; this is what I call the “design by accretion runaway syndrome”. At this point, the only option for a developer is to toss everything out of the window in disgust, and begin from scratch; which means time, effort, and skills needed for features and bug-fixing are instead spent on rewriting stuff. One way to stop a “design by accretion” before it reaches its “runaway syndrome” is to check for hacks, kludges, ugly portions of code and exterminate them at each iterations. Hacks are what they are: they might be clever, well-thought or a demostration of coding skills; but they are hacks nonetheless, and the shorter they live, the better codebase will result in the end.

The dictionary application and applet is the best result of this particular software design “technique”; not only the high-level code is a collection of circular references and inclusions – also the low-level implementation of the dictionary protocol has become hackish enough to include at least two API, one of which is an almost complete implementation of RFC 2229, while the other is like a remnant of a previous implementation.

I tried to put a thin GObject layer around it, in order to avoid having to write my own implementation of the dictionary protocol; so far, the results are discouraging. Ergo, the best solution is to throw away the low-level stuff, create a new, GObject-oriented implementation of the dictionary protocol, and build up from there.

In the meantime, I’ll have to pass a couple of exams, and begin porting the BookmarkFile and RecentManager/RecentChooser code under GLib and GTK; also, the FileChooser code should be adapted to support recently used files in OPEN mode, and the shortcuts be saved using the BookmarkFile object inside a default location – something like $XDG_DATA_HOME/desktop-bookmarks/gtk-shortcuts.xbel.

Dictionary Applet

With almost everyone in Boston, both the mailing lists and IRC channels are really quiet; feels a little weird – but I think it’s mostly envy for not being able to go to the other side of the pond and hack away.

Thus, staying here means choosing between 1. cleaning the house with Marta and 2. hacking on the dictionary applet. While the first option would significantly improve my life, the second option is the most tempting. I’ll be a good boy, though, and clean up, now that I finally found to what I’m allergic to (you wouldn’t believe it: absynthe pollen; a potential career as “damned poet” thrown out of the window). Tomorrow I’m going to stay eight hours at the university campus, so I’ll end up working on the dictionary applet anyway…

As a side project, I’ll have to update the desktop bookmarks specification. The new revision should take into account not only the storage format, but the file locations for recently used resources, desktop places and shortcuts; by defining locations and launch behaviour we will be able to create a standard way to access bookmarks to resources on a desktop box. You could access your favourite locations inside your browser, FileChooser dialogs and Nautilus; also, your system administrator could define system-wide locations, in order to access default positions on each machine and on the network – no matter which environment you are using.