Cosimo and Denny wants to see YOU in Boston!!

Sponsored by GNOME FoundationI am really pleased to say that GNOME foundation is sponsoring me again to travel to Boston for my first hackfest after a very personally successful GUADEC.

Not only GUADEC was a success for me professionally but most importantly on a personal level since I got the opportunity to meet the persons behind those IRC nicks and email addresses I knew for so long.

I hope to see you again in Boston!

Embeding GtkBuilder UI definitions into GObject classes!

Wait, what?

Back in the day Glade used to generate code which was messy if was not handled properly, eventually everyone agreed it was better to use libglade instead and load the UI interface from a xml file, something that got consolidated with the advent of GtkBuilder in GTK+

So if it’s better for applications, shouldn’t be the same for classes?

I believe so, this is why I am continuing the work that Tristan started in composite-widget-templates GTK+ branch  in my own branch named composite-template.

The idea is pretty simple,  instead of hand coding composite children in _init(), like most widget classes do or in _constructor() where they should all we need to do is set a template in _class_init() as follow

gtk_container_class_set_template (container_class,
                                  "foobar.ui",
                                  GTK_CONTAINER_TEMPLATE_FILE);

and voilà! GtkContainer class will build your children from that template using GtkBuilder at construction time. If you need to expose an internal child then all you have to do is declare it as follow

gtk_container_class_set_template (container_class,
                                  "foobar.ui",
                                  GTK_CONTAINER_TEMPLATE_FILE);
gtk_container_class_declare_internal_child
    (container_class, TRUE,
     G_STRUCT_OFFSET (FooBarPrivate, vbox),
     "vbox");

And if you do not like the idea of depending on a file, like I do, for your widget class to work you can use a string or even better a GResource.

but Juan, what about performance?

Obviously there is some extra overhead in the parsing but is not going to be significant in a medium/big size project that already uses GtkBuilder for it’s main interface. That being said if we want to optimize things the easy way would be to use EXI (Efficient XML Interchange) format which is basically a XML binary format, it would save use some memory space and the need to actually parse the UI definition. The hard way  would be to rethink GtkBuilder internal data model and transform it into a binary format that can be saved persistently. Implementing either of these approaches at the GMarkup level would not only be helpful for composite classes but more importantly for applications with big UI files.

Really wait, Say that again!

Ok, I should probably had started with an example, lets say you have a login dialog like this

With the following C source: foo-login-dialog.h foo-login-dialog.c

This code show the differences implementing a composite object the regular way and using templates. Note that USE_TEMPLATE macro is used to choose either implementation at compile time.

#include "foo-login-dialog.h"
 
struct _FooLoginDialogPrivate
{
  GtkWidget *username_entry;
  GtkWidget *password_entry;
};
 
enum
{
  LOGIN,
 
  LAST_SIGNAL
};
 
static guint login_dialog_signals[LAST_SIGNAL] = { 0 };
 
G_DEFINE_TYPE (FooLoginDialog, foo_login_dialog, GTK_TYPE_DIALOG);
 
void
on_dialog_response (FooLoginDialog *dialog, gint response_id)
{
  FooLoginDialogPrivate *priv = dialog->priv;
  const gchar *username, *password;
 
  if (response_id != GTK_RESPONSE_OK) return;
 
  username = gtk_entry_get_text (GTK_ENTRY (priv->username_entry));
  password = gtk_entry_get_text (GTK_ENTRY (priv->password_entry));
 
  g_signal_emit (dialog, login_dialog_signals[LOGIN], 0,
                 username, password);
}
 
static void
foo_login_dialog_init (FooLoginDialog *dialog)
{
  FooLoginDialogPrivate *priv;
 
  priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
                                      FOO_TYPE_LOGIN_DIALOG,
                                      FooLoginDialogPrivate);
  dialog->priv = priv;
 
#ifndef USE_TEMPLATE
  GtkWidget *content_area;
  GtkWidget *action_area;
  GtkWidget *grid;
  GtkWidget *label;
  GtkWidget *button;
 
  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
 
  grid = gtk_grid_new ();
  g_object_set (grid, "margin", 6, NULL);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
  gtk_grid_set_row_spacing (GTK_GRID (grid), 4);
  gtk_box_pack_start (GTK_BOX (content_area), grid, TRUE, TRUE, 0);
 
  label = gtk_label_new ("User name:");
  gtk_widget_set_halign (label, GTK_ALIGN_END);
  gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
  priv->username_entry = gtk_entry_new ();
  gtk_widget_set_hexpand (priv->username_entry, TRUE);
  gtk_grid_attach (GTK_GRID (grid), priv->username_entry,
                   1, 0, 1, 1);
 
  label = gtk_label_new ("Password:");
  gtk_widget_set_halign (label, GTK_ALIGN_END);
  gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
  priv->password_entry = gtk_entry_new ();
  gtk_widget_set_hexpand (priv->password_entry, TRUE);
  gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry),
                            FALSE);
  gtk_entry_set_activates_default (GTK_ENTRY (priv->password_entry),
                                   TRUE);
  gtk_grid_attach (GTK_GRID (grid), priv->password_entry,
                   1, 1, 1, 1);
 
  button = gtk_button_new_from_stock ("gtk-cancel");
  gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
                                GTK_RESPONSE_CANCEL);
 
  button = gtk_button_new_from_stock ("gtk-ok");
  gtk_widget_set_can_default (button, TRUE);
  gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
                                GTK_RESPONSE_OK);
  gtk_widget_grab_default (button);
 
  g_signal_connect_swapped (priv->username_entry, "activate",
                            G_CALLBACK (gtk_widget_grab_focus),
                            priv->password_entry);
 
  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
  g_signal_connect (dialog, "response",
                    G_CALLBACK (on_dialog_response),
                    NULL);
#endif
}
 
static void
foo_login_dialog_finalize (GObject *object)
{
  G_OBJECT_CLASS (foo_login_dialog_parent_class)->finalize (object);
}
 
static void
foo_login_dialog_login (FooLoginDialog *self,
                        const gchar *username,
                        const gchar *password)
{
  g_message ("%s user: [%s] password: [%s]",
             __func__, username, password);
}
 
static void
foo_login_dialog_class_init (FooLoginDialogClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkDialogClass *parent_class = GTK_DIALOG_CLASS (klass);
 
  g_type_class_add_private (klass, sizeof (FooLoginDialogPrivate));
 
  object_class->finalize = foo_login_dialog_finalize;
 
  klass->login = foo_login_dialog_login;
 
  login_dialog_signals[LOGIN] =
    g_signal_new ("login",
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (FooLoginDialogClass, login),
                  NULL, NULL,
                  NULL,
                  G_TYPE_NONE, 2,
                  G_TYPE_STRING, G_TYPE_STRING);
 
#ifdef USE_TEMPLATE
  gtk_container_class_set_template (GTK_CONTAINER_CLASS (klass),
                                    "foologindialog.ui",
                                    GTK_CONTAINER_TEMPLATE_FILE);
  gtk_container_class_declare_internal_child
    (GTK_CONTAINER_CLASS (klass), TRUE,
     G_STRUCT_OFFSET (FooLoginDialogPrivate, username_entry),
     "username_entry");
  gtk_container_class_declare_internal_child
    (GTK_CONTAINER_CLASS (klass), TRUE,
     G_STRUCT_OFFSET (FooLoginDialogPrivate, password_entry),
     "password_entry");
#endif  
}
 
GtkDialog *
foo_login_dialog_new ()
{
  return g_object_new (FOO_TYPE_LOGIN_DIALOG, NULL);
}

main.c: a simple program using such class, this is included in foo-login-dialog.c for convenience

#include <gtk/gtk.h>
#include "foo-login-dialog.h"
 
int main (int argc, char **argv)
{
  GtkDialog *dialog;
 
  gtk_init (&argc, &argv);
 
  dialog = foo_login_dialog_new ();
  gtk_widget_show_all (GTK_WIDGET (dialog));
 
  gtk_dialog_run (dialog);
}

You can compile it with a line like this

gcc -export-dynamic -o foo-login-dialog main.c foo-login-dialog.c \
 `pkg-config --libs --cflags gtk+-3.0` -DUSE_TEMPLATE

GtkBuilder template “foologindialog.ui”

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <template class="FooLoginDialog" parent="GtkDialog" id="this">
    <property name="can_focus">False</property>
    <property name="border_width">5</property>
    <property name="type_hint">dialog</property>
    <signal name="response" handler="on_dialog_response" swapped="no"/>
    <child internal-child="vbox">
      <object id="dialog-vbox1">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object id="dialog-action_area1">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
            <child>
              <object id="button1">
                <property name="label">gtk-cancel</property>
                <property name="use_action_appearance">False</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_action_appearance">False</property>
                <property name="use_stock">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object id="button2">
                <property name="label">gtk-ok</property>
                <property name="use_action_appearance">False</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="can_default">True</property>
                <property name="has_default">True</property>
                <property name="receives_default">True</property>
                <property name="use_action_appearance">False</property>
                <property name="use_stock">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="pack_type">end</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object id="grid1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="row_spacing">4</property>
            <property name="column_spacing">6</property>
            <child>
              <object id="label1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="halign">end</property>
                <property name="label" translatable="yes">User name:</property>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">0</property>
                <property name="width">1</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <object id="label2">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="halign">end</property>
                <property name="label" translatable="yes">Password:</property>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">1</property>
                <property name="width">1</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <object id="username_entry">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hexpand">True</property>
                <property name="invisible_char"></property>
                <signal name="activate" handler="gtk_widget_grab_focus" object="password_entry" swapped="yes"/>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">0</property>
                <property name="width">1</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <object id="password_entry">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hexpand">True</property>
                <property name="visibility">False</property>
                <property name="invisible_char"></property>
                <property name="activates_default">True</property>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">1</property>
                <property name="width">1</property>
                <property name="height">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
    <action-widgets>
      <action-widget response="-6">button1</action-widget>
      <action-widget response="-5">button2</action-widget>
    </action-widgets>
  </template>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<interface>

Since this example sets the template from a relative path the file foologindialog.ui has to be in the current working directory when you run the program.
And last but not least you need composite-templates Gtk+ branch for this to work

$ git clone git://git.gnome.org/gtk+
$ cd gtk+
$ git checkout -b composite-templates origin/composite-templates

Cross compiling Glade for win32

It was a long time since I did not give cross compiling a try and it turns out to be easier than expected if you use precompiled binaries from windows:mingw[1] project on the OpenSUSE Build Service. (OBS)

First of all we need to install the cross compiler, in Debian and Debian based distros do

$ sudo apt-get install mingw-w64

Then we need to download a python script [2] that makes downloading mingw packages from OBS easy.

wget https://github.com/mkbosmans/download-mingw-rpm/raw/master/download-mingw-rpm.py

Now we can download the dependencies (hicolor-icon-theme is only needed at runtime)

python3 download-mingw-rpm.py --deps gtk3-devel libxml2-devel \
        hicolor-icon-theme

This will unpack everything under usr/i686-w64-mingw32/sys-root/mingw

Next we need to setup some variables for the cross compilation

export CC=i686-w64-mingw32-gcc
export MINGW_ROOT=`readlink -f usr/i686-w64-mingw32/sys-root/mingw`
export PKG_CONFIG_LIBDIR=$MINGW_ROOT/lib/pkgconfig
export PKG_CONFIG_PATH=$MINGW_ROOT/share/pkgconfig
export PKG_CONFIG="pkg-config --define-variable=prefix=$MINGW_ROOT"

CC: the cross compiler
MINGW_ROOT: is an absolute path we are going to use as a prefix for our build
PKG_CONFIG_LIBDIR: this way we tell pkg-config to use mingw libs
PKG_CONFIG_PATH: just to make sure we are not pointing some where else
PKG_CONFIG: ask pkg-config to replace .pc files prefix variable with the real path

And this is pretty much it, now we can proceed to run autogen.sh with –host and –target flags and glade should compile

git clone git://git.gnome.org/glade
cd glade
./autogen.sh --prefix=$MINGW_ROOT --host i686-w64-mingw32 \
--target i686-w64-mingw32 --enable-shared=yes --enable-static=no
make
make install

Before you can run it you need to update glib schemas bin file

glib-compile-schemas $MINGW_ROOT/share/glib-2.0/schemas

BTW there is a scrip file that automates this process and creates a windows installer using nsis
in glade sources under build/mingw-w64 directory.

1) https://build.opensuse.org/project/show?project=windows%3Amingw%3Awin32
2) https://github.com/mkbosmans/download-mingw-rpm

SVG && cairo_path_t fun!

For all of those of you who usually program with cairo: How do you include a somewhat complex path in your code?

Let’s take GIMP as an example…

Wilber as seen on GIMP's empty windows

Wilber figure shows ups in a few places and the cairo code to draw it is in one place

http://git.gnome.org/browse/gimp/tree/app/widgets/gimpcairo-wilber.c

The important function here is gimp_cairo_wilber() which uses a path created from a SVG path description string. This allows them to grab that string and paste it inside a SVG file and edit it with Inkscape for example. Which could be annoying if you have to do it frequently or if it has to be done by an artist that does not feel comfortable messing around with source code.

This is the SVG path for wilber (WordPress does not allow me to include a svg image)

 <svg width="225" height="165">
  <g transform="translate(-287,-438)">
    <path id="path2987" d="m 509.72445,438.68864 c -8.24739,31.09081 -44.77407,52.85702 -77.8653,59.0601 6.66245,5.26814 11.01867,13.47826 11.01867,22.62501 1e-5,15.87371 -12.92175,28.64855 -28.79547,28.64855 -15.87372,0 -28.79547,-12.77484 -28.79547,-28.64855 0,-8.84972 3.98978,-16.76089 10.2841,-22.03735 -36.20146,-2.43256 -51.86122,-34.37828 -51.86123,-34.37826 l -1.02841,45.69077 c 0,4.7013 -0.59743,10.31207 -2.49756,18.65829 -0.33714,-0.50356 -0.66979,-0.97205 -1.02841,-1.46916 -8.90026,-12.33694 -21.90268,-19.02373 -32.7622,-18.07063 -3.61983,0.3177 -6.923,1.56607 -9.84335,3.67289 -11.68135,8.42727 -11.57317,28.50691 0.29384,44.9562 10.11908,14.02637 25.47866,20.85962 37.02274,17.33604 58.07995,40.4437 198.30291,67.68661 175.85805,-136.0439 z M 363.24953,501.1278 c 10.58249,-2e-5 19.24596,8.66347 19.24596,19.24595 0,10.58249 -8.66348,19.09904 -19.24596,19.09904 -10.58247,0 -19.09903,-8.51655 -19.09903,-19.09904 -1e-5,-10.58246 8.51656,-19.24595 19.09903,-19.24595 z m -57.44402,14.9854 c 5.87915,-2e-5 10.57793,5.72665 10.57793,12.78166 10e-6,7.05496 -4.69877,12.78166 -10.57793,12.78166 -5.87915,0 -10.72484,-5.72665 -10.72484,-12.78166 -2e-5,-7.05501 4.84569,-12.78166 10.72484,-12.78166 z M 440.821,552.54828 c 0,0 7.9294,1.4756 13.0755,6.90504 3.52231,3.71619 3.85558,9.70174 3.08522,17.92371 -0.77029,-3.49373 -2.08601,-5.61044 -3.08522,-8.08037 -10.88262,13.17996 -40.46669,13.79263 -77.8653,0.58767 40.60128,8.1206 61.35686,0.67581 73.45783,-8.66803 -3.1952,-4.12713 -8.66803,-8.66802 -8.66803,-8.66802 z m -6.17377,-27.95144 c 0,7.6429 -6.20294,13.84584 -13.84584,13.84584 -7.6429,0 -13.84584,-6.20294 -13.84584,-13.84584 0,-7.6429 6.20294,-13.84584 13.84584,-13.84584 7.6429,0 13.84584,6.20294 13.84584,13.84584 z m -56.6468,-1.59753 c 0,4.70333 -3.81719,8.52053 -8.52052,8.52053 -4.70333,0 -8.52052,-3.8172 -8.52052,-8.52053 0,-4.70332 3.81719,-8.52052 8.52052,-8.52052 4.70333,0 8.52052,3.8172 8.52052,8.52052 z"/>
  </g>
</svg

So after doing the same thing a few times for Glade

Glade while loading a big file

I decided to automate the process by creating a simple application that takes a SVG file and outputs C code for a cairo_path_t struct.
Generated header and source file:

#ifndef __WILBER_H__
#define __WILBER_H__
 
#define WILBER_WIDTH 225.000000
#define WILBER_HEIGHT 165.000000
extern cairo_path_t wilber_path;
 
#endif /* __WILBER_H__ */
#include <cairo.h>

static cairo_path_data_t wilber_data[] = {
	{.header.type = 0, .header.length = 2},
	{.point.x = 509.724450, .point.y = 438.688640},
	{.header.type = 2, .header.length = 4},
	{.point.x = 501.477060, .point.y = 469.779450},
	{.point.x = 464.950380, .point.y = 491.545660},
	{.point.x = 431.859150, .point.y = 497.748740},
	{.header.type = 2, .header.length = 4},
	{.point.x = 438.521600, .point.y = 503.016880},
	{.point.x = 442.877820, .point.y = 511.227000},
	{.point.x = 442.877820, .point.y = 520.373750},
	{.header.type = 2, .header.length = 4},
	{.point.x = 442.877830, .point.y = 536.247460},
	{.point.x = 429.956070, .point.y = 549.022300},
	{.point.x = 414.082350, .point.y = 549.022300},
	{.header.type = 2, .header.length = 4},
	{.point.x = 398.208630, .point.y = 549.022300},
	{.point.x = 385.286880, .point.y = 536.247460},
	{.point.x = 385.286880, .point.y = 520.373750},
	{.header.type = 2, .header.length = 4},
	{.point.x = 385.286880, .point.y = 511.524030},
	{.point.x = 389.276660, .point.y = 503.612860},
	{.point.x = 395.570980, .point.y = 498.336400},
	{.header.type = 2, .header.length = 4},
	{.point.x = 359.369520, .point.y = 495.903840},
	{.point.x = 343.709760, .point.y = 463.958120},
	{.point.x = 343.709750, .point.y = 463.958140},
	{.header.type = 1, .header.length = 2},
	{.point.x = 342.681340, .point.y = 509.648910},
	{.header.type = 2, .header.length = 4},
	{.point.x = 342.681340, .point.y = 514.350210},
	{.point.x = 342.083910, .point.y = 519.960980},
	{.point.x = 340.183780, .point.y = 528.307200},
	{.header.type = 2, .header.length = 4},
	{.point.x = 339.846640, .point.y = 527.803640},
	{.point.x = 339.513990, .point.y = 527.335150},
	{.point.x = 339.155370, .point.y = 526.838040},
	{.header.type = 2, .header.length = 4},
	{.point.x = 330.255110, .point.y = 514.501100},
	{.point.x = 317.252690, .point.y = 507.814310},
	{.point.x = 306.393170, .point.y = 508.767410},
	{.header.type = 2, .header.length = 4},
	{.point.x = 302.773340, .point.y = 509.085110},
	{.point.x = 299.470170, .point.y = 510.333480},
	{.point.x = 296.549820, .point.y = 512.440300},
	{.header.type = 2, .header.length = 4},
	{.point.x = 284.868470, .point.y = 520.867570},
	{.point.x = 284.976650, .point.y = 540.947210},
	{.point.x = 296.843660, .point.y = 557.396500},
	{.header.type = 2, .header.length = 4},
	{.point.x = 306.962740, .point.y = 571.422870},
	{.point.x = 322.322320, .point.y = 578.256120},
	{.point.x = 333.866400, .point.y = 574.732540},
	{.header.type = 2, .header.length = 4},
	{.point.x = 391.946350, .point.y = 615.176240},
	{.point.x = 532.169310, .point.y = 642.419150},
	{.point.x = 509.724450, .point.y = 438.688640},
	{.header.type = 3, .header.length = 1},
	{.header.type = 0, .header.length = 2},
	{.point.x = 363.249530, .point.y = 501.127800},
	{.header.type = 2, .header.length = 4},
	{.point.x = 373.832020, .point.y = 501.127780},
	{.point.x = 382.495490, .point.y = 509.791270},
	{.point.x = 382.495490, .point.y = 520.373750},
	{.header.type = 2, .header.length = 4},
	{.point.x = 382.495490, .point.y = 530.956240},
	{.point.x = 373.832010, .point.y = 539.472790},
	{.point.x = 363.249530, .point.y = 539.472790},
	{.header.type = 2, .header.length = 4},
	{.point.x = 352.667060, .point.y = 539.472790},
	{.point.x = 344.150500, .point.y = 530.956240},
	{.point.x = 344.150500, .point.y = 520.373750},
	{.header.type = 2, .header.length = 4},
	{.point.x = 344.150490, .point.y = 509.791290},
	{.point.x = 352.667060, .point.y = 501.127800},
	{.point.x = 363.249530, .point.y = 501.127800},
	{.header.type = 3, .header.length = 1},
	{.header.type = 0, .header.length = 2},
	{.point.x = 305.805510, .point.y = 516.113200},
	{.header.type = 2, .header.length = 4},
	{.point.x = 311.684660, .point.y = 516.113180},
	{.point.x = 316.383440, .point.y = 521.839850},
	{.point.x = 316.383440, .point.y = 528.894860},
	{.header.type = 2, .header.length = 4},
	{.point.x = 316.383450, .point.y = 535.949820},
	{.point.x = 311.684670, .point.y = 541.676520},
	{.point.x = 305.805510, .point.y = 541.676520},
	{.header.type = 2, .header.length = 4},
	{.point.x = 299.926360, .point.y = 541.676520},
	{.point.x = 295.080670, .point.y = 535.949870},
	{.point.x = 295.080670, .point.y = 528.894860},
	{.header.type = 2, .header.length = 4},
	{.point.x = 295.080650, .point.y = 521.839850},
	{.point.x = 299.926360, .point.y = 516.113200},
	{.point.x = 305.805510, .point.y = 516.113200},
	{.header.type = 3, .header.length = 1},
	{.header.type = 0, .header.length = 2},
	{.point.x = 440.821000, .point.y = 552.548280},
	{.header.type = 2, .header.length = 4},
	{.point.x = 440.821000, .point.y = 552.548280},
	{.point.x = 448.750400, .point.y = 554.023880},
	{.point.x = 453.896500, .point.y = 559.453320},
	{.header.type = 2, .header.length = 4},
	{.point.x = 457.418810, .point.y = 563.169510},
	{.point.x = 457.752080, .point.y = 569.155060},
	{.point.x = 456.981720, .point.y = 577.377030},
	{.header.type = 2, .header.length = 4},
	{.point.x = 456.211430, .point.y = 573.883300},
	{.point.x = 454.895710, .point.y = 571.766590},
	{.point.x = 453.896500, .point.y = 569.296660},
	{.header.type = 2, .header.length = 4},
	{.point.x = 443.013880, .point.y = 582.476620},
	{.point.x = 413.429810, .point.y = 583.089290},
	{.point.x = 376.031200, .point.y = 569.884330},
	{.header.type = 2, .header.length = 4},
	{.point.x = 416.632480, .point.y = 578.004930},
	{.point.x = 437.388060, .point.y = 570.560140},
	{.point.x = 449.489030, .point.y = 561.216300},
	{.header.type = 2, .header.length = 4},
	{.point.x = 446.293830, .point.y = 557.089170},
	{.point.x = 440.821000, .point.y = 552.548280},
	{.point.x = 440.821000, .point.y = 552.548280},
	{.header.type = 3, .header.length = 1},
	{.header.type = 0, .header.length = 2},
	{.point.x = 434.647230, .point.y = 524.596840},
	{.header.type = 2, .header.length = 4},
	{.point.x = 434.647230, .point.y = 532.239740},
	{.point.x = 428.444290, .point.y = 538.442680},
	{.point.x = 420.801390, .point.y = 538.442680},
	{.header.type = 2, .header.length = 4},
	{.point.x = 413.158490, .point.y = 538.442680},
	{.point.x = 406.955550, .point.y = 532.239740},
	{.point.x = 406.955550, .point.y = 524.596840},
	{.header.type = 2, .header.length = 4},
	{.point.x = 406.955550, .point.y = 516.953940},
	{.point.x = 413.158490, .point.y = 510.751000},
	{.point.x = 420.801390, .point.y = 510.751000},
	{.header.type = 2, .header.length = 4},
	{.point.x = 428.444290, .point.y = 510.751000},
	{.point.x = 434.647230, .point.y = 516.953940},
	{.point.x = 434.647230, .point.y = 524.596840},
	{.header.type = 3, .header.length = 1},
	{.header.type = 0, .header.length = 2},
	{.point.x = 378.000430, .point.y = 522.999310},
	{.header.type = 2, .header.length = 4},
	{.point.x = 378.000430, .point.y = 527.702640},
	{.point.x = 374.183240, .point.y = 531.519840},
	{.point.x = 369.479910, .point.y = 531.519840},
	{.header.type = 2, .header.length = 4},
	{.point.x = 364.776580, .point.y = 531.519840},
	{.point.x = 360.959390, .point.y = 527.702640},
	{.point.x = 360.959390, .point.y = 522.999310},
	{.header.type = 2, .header.length = 4},
	{.point.x = 360.959390, .point.y = 518.295990},
	{.point.x = 364.776580, .point.y = 514.478790},
	{.point.x = 369.479910, .point.y = 514.478790},
	{.header.type = 2, .header.length = 4},
	{.point.x = 374.183240, .point.y = 514.478790},
	{.point.x = 378.000430, .point.y = 518.295990},
	{.point.x = 378.000430, .point.y = 522.999310},
	{.header.type = 3, .header.length = 1}
};

cairo_path_t wilber_path = {0, wilber_data, 160};

Which can be easily integrated in the Makefile system with a few rules like

# Rules to generate cairo paths
%.h: %.svg
  cairo_svg2path $< --target=$@
%.c: %.svg
  cairo_svg2path $< --target=$@

Then all you have to do is include the corresponding header and use
cairo_append_path (cr, &wilber_path);
to append the path to a cairo context.

You can find cairo_svg2path source code in cairo bugzilla page
https://bugs.freedesktop.org/show_bug.cgi?id=50363
Enjoy!

Hacking Glade in Glade: A recursion exercise

Last week, inspired by Tristan’s GtkComposite branch idea of embedding builder xml into widget classes together with the old eagerness  of implementing Glade UI with Glade itself I started hacking Glade in glade!

The first step was to recreate GladeWindow widget hierarchy in Glade, that was quick and easy. Then I started replacing the hard coded widgets with it but since I wanted to use the very same executable I was hacking on to edit its own UI definition file I had to make sure I got the palette, design view, inspector and property editor properly working before removing the old code.

Later on I proceeded to remove the old menu that was implemented using GtkUIManager… big mistake! How was I supposed to edit the file if I was not able to save the file, so I revert it and moved it to the end of the window and keep it until the new menu had a working save menu item!

After this, Glade was functional and only had one regression. For some reason GtkAccelLabel does not show up the accelerator when used with a related action.  But that seems to be a Gtk+ bug (see bugzilla report)

The only problem left to solve was the dependency GladeWindow had on the UI file. I really did not like the idea of a class depending on a file so I decided it was a good time to give GResource a try! GResource allows you to easily embed resources into your code. All you have to do is define all your resources in GResource xml format, in this case only one file glade.glade.

glade-resources.gresource.xml:

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
 <gresource prefix="/org/gnome/glade">
  <file compressed="true">glade.glade</file>
 </gresource>
</gresources>

And compile it into source using glib-compile-resourses tool, which can be done automatically with a couple of Makefile rules.

# Rules to compile resources
%.h: %.gresource.xml
  $(GLIB_COMPILE_RESOURCES) --generate $< --target=$@
%.c: %.gresource.xml
  $(GLIB_COMPILE_RESOURCES) --generate $< --target=$@
 
# Dependencies rule, and do not forget to do a make after
# editing the UI with glade!
glade-resources.c glade-resources.h: \
       glade-resources.gresource.xml \
       $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies \
         glade-resources.gresource.xml)

Once you have the build system setup it is as simply as using the resource uri instead of a file path.

GtkBuilder *builder = gtk_builder_new ();
GError *error = NULL;
gtk_builder_add_from_resource (builder,
                               "/org/gnome/glade/glade.glade",
                               error);

As usual you can find this in git master
git clone git://git.gnome.org/glade

New Glade edit modes!

I am happy to announce two new Glade features that should make UI designers life a little bit easier. As you probably know Gtk+ 3 introduced new widget properties to control margins and its alignments which where only accessible in Glade  through the common tab in the property editor… until now!. Margins and Alignment edit modes join good old Selection and Drag modes to let you edit margins and alignment properties using nothing but the mouse. After all having offscreen project widgets not only lets us draw pretty selections but also do something useful as well.

You can currently find this code in git master soon to be released as Glade 3.11

UPDATE: Glade 3.11 is on the wild! For more information see the release notes.

git clone git://git.gnome.org/glade

[vimeo width=”640″ height=”352″]http://vimeo.com/31572206[/vimeo]

Margins edit mode:

Using this mode is as easy as clicking on the border of a selected widget to enable it and then use the mouse to simply modify the desired margin. Also to make it easier to set the same value in both margins you can hold down shift and holding down control makes it snap to 6 pixels 😉

Alignments edit mode:

This mode can be enabled by selecting it from the toolbar or pressing Shift+left click on the selected widget border. Clicking over the pushpins will make it toggle so for example if you want to have a widget left aligned all you have to do is unpush the right pushpin, to make the widget vertically centered both top and bottom pushpin have to be unpush.