Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GTK+ Programming using Vala

GTK+ Programming using Vala

GTK+ Programming using Vala

David Wu

March 30, 2011
Tweet

More Decks by David Wu

Other Decks in Programming

Transcript

  1. 請先安裝 sudo apt-get install build-essential libgtk2.0-dev valac glade libglade2-dev 按照網頁步驟安裝

    valac: https://help.ubuntu.com/community/Vala 結束後檢查 pkg-config --cflags --libs gtk+-2.0
  2. Installing Vala  sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 7DAAC99C

     sudo add-apt-repository ppa:vala-team  sudo apt-get update  sudo apt-get install valac vala-utils vala-doc valac-dbg
  3. GTK+  ( graphical | widget ) ( toolkit |

    framework ) to make your life easy Client Programs Xlib X Server GTK+
  4. Supporting Libraries GTK+ GLib GObject GIO GDK GdkPixBuf Pango ATK

    Cairo X Server Client Program * Disclaimer: not exactly accurate, just an oversimplified conceptual view http://www.gtk.org/documentation.html
  5. Cross Platform & Multi-Languages  Linux  Windows  Mac

     Web!!  C  C++ (Gtkmm)  C# (Gtk#)  Python (PyGTK)  Perl (Gtk2-Perl)  PHP (PHP-GTK)  Java (Java-Gnome)  Vala
  6. Hello World (v1) #include <gtk/gtk.h> int main(int argc, char *argv[])

    { GtkWidget *window; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Hello World"); gtk_widget_show(window); gtk_main(); return 0; } 1.helloworld.c
  7. Hello World (v1) #include <gtk/gtk.h> int main(int argc, char *argv[])

    { GtkWidget *window; gtk_init(&argc, &argv); // initializing GTK+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Hello World"); gtk_widget_show(window); gtk_main(); return 0; } 1.helloworld.c
  8. Hello World (v1) #include <gtk/gtk.h> int main(int argc, char *argv[])

    { GtkWidget *window; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Hello World"); gtk_widget_show(window); gtk_main(); return 0; } 1.helloworld.c
  9. Widget Hierarchy GObject GinitiallyUnowned GtkObject GtkWidget GtkContainer GtkBin GtkWindow Provides

    object-oriented capability – signal systems, properties, etc. Floating referenced object Base class for all GTK+ objects Abstract base class for all widgets – style properties and standard functions Abstract class for all container widgets Abstract class for widgets containing only 1 child Use GTK+ documentation to find the object hierarchy, properties, and signals of widgets:
  10. Hello World (v1) #include <gtk/gtk.h> int main(int argc, char *argv[])

    { GtkWidget *window; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Hello World"); gtk_widget_show(window); gtk_main(); // starts main loop return 0; } 1.helloworld.c
  11. Hello World (v2) int main(int argc, char *argv[]) { GtkWidget

    *window, *label; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // . . . . . g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); label = gtk_label_new("Hello World"); gtk_label_set_selectable(GTK_LABEL(label), TRUE); gtk_container_add(GTK_CONTAINER(window), label); gtk_widget_show_all(window); gtk_main(); return 0; } static void destroy(GtkWidget *window, gpointer data) { gtk_main_quit(); } static gboolean delete_event(GtkWidget *window, GdkEvent *event, gpointer data) { return FALSE; } 2.helloworld.c
  12. Hello World (v2) int main(int argc, char *argv[]) { GtkWidget

    *window, *label; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // . . . . . g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); label = gtk_label_new("Hello World"); gtk_label_set_selectable(GTK_LABEL(label), TRUE); gtk_container_add(GTK_CONTAINER(window), label); gtk_widget_show_all(window); gtk_main(); return 0; } static void destroy(GtkWidget *window, gpointer data) { gtk_main_quit(); } static gboolean delete_event(GtkWidget *window, GdkEvent *event, gpointer data) { return FALSE; } 2.helloworld.c
  13. Signals and Callbacks  Signal connection gulong g_signal_connect (gpointer object,

    const gchar *signal_name, GCallback handler, gpointer data);  Callback functions static void callback_func (GtkWidget *widget, /* Other possible arguments */ ..., gpointer data);
  14. Hello World (v2) int main(int argc, char *argv[]) { GtkWidget

    *window, *label; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // . . . . . g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); // . . . . . gtk_main(); return 0; } static void destroy(GtkWidget *window, gpointer data) { gtk_main_quit(); } static gboolean delete_event(GtkWidget *window, GdkEvent *event, gpointer data) { return FALSE; } 2.helloworld.c
  15. Events and Callbacks  Callback functions static gboolean callback_func (GtkWidget

    *widget, GdkEvent *event, gpointer data); static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { gboolean answer = /* Ask the user if exiting is desired */ return !answer; }
  16. Event Types & Event Structures  Event types static gboolean

    delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { return ( event->type != GDK_DELETE ); }  Event structures g_signal_connect (G_OBJECT(widget), “key-press-event”, G_CALLBACK(key_press), NULL); static gboolean key_press (GtkWidget *widget, GdkEventKey *event, gpointer data);
  17. Widget Properties  g_object_set() and g_object_get() g_object_set (button, “focus-on-click”, TRUE,

    NULL);  Monitoring widget properties g_signal_connect (G_OBJECT(button), “notify::focus-on-click”, G_CALLBACK(property_changed), NULL); static void property_changed (GtkWidget *widget, GParamSpec *property, gpointer data) { /* handle the property change */ }
  18. Vala  valac --pkg gtk+-2.0 ./*.vala -C -H ./*.h 

    valac --pkg gtk+-2.0 [--pkg gmodule-2.0] ./*.vala *.vala *.c *.h valac
  19. Hello World (v3) using Gtk; public class Application : GLib.Object

    { public static int main(string[] args) { Gtk.init(ref args); var window = new Window(WindowType.TOPLEVEL); window.set_title("Hello World!"); window.set_border_width(10); window.set_size_request(200, 100); window.destroy.connect(Gtk.main_quit); window.delete_event.connect((event) => { return false; }); var label = new Label("Hello World"); label.set_selectable(true); window.add(label); window.show_all(); Gtk.main(); return 0; } } 3.helloworld.vala
  20. Vala Data Types  Value types  char, uchar, unichar

     int, uint, long, ulong, short, ushort  int8, int16, in32, int64, uint8, uint16, uint32, uint64  float, double  bool (true, false)  struct, enum  string  Arrays  Reference types
  21. string  string greeting = “hello, world”;  string s1

    = greeting[7:12]; // “world”  string s2 = greeting[-4:-2]; // “or”  bool b = “false”.to_bool();  int i = “-52”.to_int();  double d = “6.67428E-11”.to_double();  string s1 = true.to_string(); // “true”  string s2 = 21.to_string(); // “21”  string s3 = “%i: %s”.printf(8, “eight”); // “8: eight”
  22. Reference Types /* defining a class */ class Track :

    GLib.Object { /* subclassing 'GLib.Object' */ public double mass; /* a public field */ public double name { get; set; } /* a public property */ private bool terminated = false; /* a private field */ public void terminate() { /* a public method */ terminated = true; } }
  23. Anonymous Methods / Closures (a) => { stdout.printf(“%d\n”, a); }

    // Property monitoring obj.notify.connect((source, property) => { stdout.printf(“Property %s has changed!”, property.name); }); // Monitoring only a single property alice.notify[“age”].connect((s, p) => { stdout.printf(“age has changed”); }); // Connecting signals with annonymous callback functions obj.destroy.connect(() => { Gtk.main_quit(); });
  24. Widgets  Container widgets  Boxes  Panes  Table

     Expander  Notebook  EventBox  Dialogs  Modal dialogs  Nonmodal dialogs  Message dialogs  Basic widgets  ToggleButton  CheckButton  RadioButton  Entry  SpinButton  Scales  TextView  Style properties  Iterators and marks  Text tags  Glib  Timeout functions
  25. Boxes public static int main(string[] args) { Gtk.init(ref args); var

    window = new Window(WindowType.TOPLEVEL); window.destroy.connect(Gtk.main_quit); // . . . . . var vbox = new VBox(false /* homogeneous */, 5 /* spacing */); var button1 = new Button.with_label("Destroy me!"); var button2 = new Button.with_label("Button 2: EXPAND, !FILL"); var button3 = new Button.with_label("Button 3"); var button4 = new Button.with_label("Button 4: !EXPAND"); var button5 = new Button.with_label("Button 5"); vbox.pack_start(button1); vbox.pack_start(button2, true, false); vbox.pack_start(button3); vbox.pack_start(button4, false); vbox.pack_start(button5); button1.clicked.connect(() => { button1.destroy(); }); window.add(vbox); window.show_all(); Gtk.main(); return 0; } 4.boxes.vala
  26. Panes public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var hpaned = new HPaned(); var button1 = new Button.with_label("Resize"); var button2 = new Button.with_label("Me!"); button1.clicked.connect(app.destroy_window); button2.clicked.connect(app.destroy_window); hpaned.add1(button1); hpaned.add2(button2); app.add_to_window(hpaned); app.show(); Gtk.main(); return 0; } 5.panes.vala
  27. Tables public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var table = new Table(2, 2, false); var label = new Label("Enter the following information..."); var label2 = new Label("Name: "); var name = new Entry(); table.attach(label, 0, 2, 0, 1, AttachOptions.EXPAND, AttachOptions.SHRINK, 0, 0); table.attach(label2, 0, 1, 1, 2, AttachOptions.SHRINK, AttachOptions.SHRINK, 0, 0); table.attach(name, 1, 2, 1, 2, AttachOptions.EXPAND | AttachOptions.FILL, AttachOptions.SHRINK, 0, 0); table.set_row_spacings(5); table.set_col_spacings(5); app.add_to_window(table); app.show(); Gtk.main(); return 0; } 6.tables.vala
  28. Expanders public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var expander = new Expander.with_mnemonic("Click _Me For More!"); var label = new Label("Hide me or show me,\nthat is your choice."); expander.add(label); expander.set_expanded(true); app.add_to_window(expander); app.show(); Gtk.main(); return 0; } 7.expanders.vala
  29. Notebooks public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var notebook = new Notebook(); var label1 = new Label("Page One"); var label2 = new Label("Page Two"); var child1 = new Button.with_label("Go to page 2 to find the answer."); var child2 = new Button.with_label("Go to page 1 to find the answer."); child1.clicked.connect(() => { switch_page(notebook); }); child2.clicked.connect(() => { switch_page(notebook); }); notebook.append_page(child1, label1); notebook.append_page(child2, label2); notebook.set_tab_pos(PositionType.BOTTOM); app.add_to_window(notebook); app.show(); Gtk.main(); return 0; } 8.notebooks.vala public static void switch_page(Notebook nb) { if ( nb.get_current_page() == 0 ) { nb.set_current_page(1); } else { nb.set_current_page(0); } }
  30. EventBox public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var eventbox = new EventBox(); var label = new Label("Double-Click Me!"); eventbox.set_above_child(false); eventbox.button_press_event.connect( (event) => { return button_pressed(event, label); }); eventbox.add(label); app.add_to_window(eventbox); eventbox.set_events(EventMask.BUTTON_PRESS_MASK); eventbox.realize(); eventbox.get_window() .set_cursor(new Cursor(CursorType.HAND1)); app.show(); Gtk.main(); return 0; } 9.eventboxes.vala public static bool button_pressed (EventButton event, Label label) { if ( event.type == EventType.2BUTTON_PRESS ) { unowned string text = label.get_text(); if ( text[0] == 'D' ) { label.set_text("I Was Double-Clicked!"); } else { label.set_text("Double-Click Me Again!"); } } return false; }
  31. ToggleButton public static void button_toggled ( ToggleButton own, ToggleButton other)

    { other.set_sensitive(!own.get_active()); } public static int main(string[] args) { Gtk.init(ref args); var app = new Application(); var vbox = new VBox(true, 5); var toggle1 = new ToggleButton.with_mnemonic("_Deactive the other one!"); var toggle2 = new ToggleButton.with_mnemonic("_No! Deactivate that one!"); toggle1.toggled.connect(() => { Application.button_toggled(toggle1, toggle2); }); toggle2.toggled.connect(() => { Application.button_toggled(toggle2, toggle1); }); vbox.pack_start(toggle1); vbox.pack_start(toggle2); app.add_to_window(vbox); app.show(); Gtk.main(); return 0; } 10.togglebuttons.vala
  32. CheckButton public static void button_toggled(ToggleButton own, ToggleButton other) { other.set_sensitive(!own.get_active());

    } public static int main(string[] args) { // . . . . . var check1 = new CheckButton.with_label("I am the main option."); var check2 = new CheckButton.with_label("I rely on the other guy."); /* Only enable the second check button when the first is enabled. */ check2.set_sensitive(false); check1.toggled.connect(() => { check2.set_sensitive(check1.get_active()); }); var close = new Button.from_stock(STOCK_CLOSE); close.clicked.connect(() => { app.destroy(); }); var vbox = new VBox(false, 5); vbox.pack_start(check1, false, true, 0); vbox.pack_start(check2, false, true, 0); vbox.pack_start(close, false, true, 0); app.add_to_window(vbox); app.show(); Gtk.main(); return 0; } 11.checkbuttons.vala
  33. RadioButton public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var radio1 = new RadioButton.with_label(null, "I want to be clicked!"); var radio2 = new RadioButton.with_label_from_widget(radio1, "Click me instead!"); var radio3 = new RadioButton.with_label_from_widget(radio1, "No! Click me!"); var radio4 = new RadioButton.with_label_from_widget(radio3, "No! Click me instead!"); var vbox = new VBox(false, 5); vbox.pack_start(radio1); vbox.pack_start(radio2); vbox.pack_start(radio3); vbox.pack_start(radio4); app.add_to_window(vbox); app.show(); Gtk.main(); return 0; } 12.radiobuttons.vala
  34. Entry public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); string str = "What is the password for " + Environment.get_user_name() + "?"; var question = new Label(str); var label = new Label("Password: "); var pass = new Entry(); pass.set_visibility(false); pass.set_invisible_char('*'); var hbox = new HBox(false, 5); hbox.pack_start(label); hbox.pack_start(pass); var vbox = new VBox(false, 5); vbox.pack_start(question); vbox.pack_start(hbox); app.add_to_window(vbox); app.show(); Gtk.main(); return 0; } 13.entries.vala
  35. SpinButton public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var integer = new Adjustment(5.0, 0.0, 10.0, 1.0, 2.0, 2.0); var float_pt = new Adjustment(0.5, 0.0, 1.0, 0.1, 0.5, 0.5); var spin_int = new SpinButton(integer, 1.0, 0); var spin_float = new SpinButton(float_pt, 0.1, 1); var vbox = new VBox(false, 5); vbox.pack_start(spin_int); vbox.pack_start(spin_float); app.add_to_window(vbox); app.show(); Gtk.main(); return 0; } 14.spinbuttons.vala
  36. Scales public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var scale_int = new HScale.with_range(0.0, 10.0, 1.0); var scale_float = new HScale.with_range(0.0, 1.0, 0.1); scale_int.set_digits(0); scale_float.set_digits(1); scale_int.set_value_pos(PositionType.RIGHT); scale_float.set_value_pos(PositionType.LEFT); var vbox = new VBox(false, 5); vbox.pack_start(scale_int); vbox.pack_start(scale_float); app.add_to_window(vbox); app.show(); Gtk.main(); return 0; } 15.scales.vala
  37. Modal Dialogs public void button_clicked(Button button) { var dialog =

    new Dialog.with_buttons("Information", this.window, DialogFlags.MODAL, STOCK_OK, ResponseType.OK); dialog.set_has_separator(false); var label = new Label("The button was clicked!"); var image = new Image.from_stock(STOCK_DIALOG_INFO, IconSize.DIALOG); var hbox = new HBox(false, 5); hbox.set_border_width(10); hbox.pack_start(image); hbox.pack_start(label); dialog.vbox.pack_start(hbox); dialog.show_all(); dialog.run(); dialog.destroy(); } public static int main(string[] args) { Gtk.init(ref args); var app = new Application(); var button = new Button.with_mnemonic("_Click me"); button.clicked.connect(() => { app.button_clicked(button); }); app.add_to_window(button); app.show(); Gtk.main(); return 0; } 16.modaldialogs.vala
  38. Non-Modal Dialogs public void button_clicked(Button button) { var dialog =

    new Dialog.with_buttons("Information", this.window, DialogFlags.DESTROY_WITH_PARENT, STOCK_OK, ResponseType.OK); dialog.set_has_separator(false); var label = new Label("The button was clicked!"); var image = new Image.from_stock(STOCK_DIALOG_INFO, IconSize.DIALOG); var hbox = new HBox(false, 5); hbox.set_border_width(10); hbox.pack_start(image); hbox.pack_start(label); dialog.vbox.pack_start(hbox); dialog.show_all(); dialog.response.connect(() => { dialog.destroy(); }); } public static int main(string[] args) { Gtk.init(ref args); var app = new Application(); var button = new Button.with_mnemonic("_Click me"); button.clicked.connect(() => { app.button_clicked(button); }); app.add_to_window(button); app.show(); Gtk.main(); return 0; } 17.nonmodaldialogs.vala
  39. MessageDialog public void button_clicked(Button button) { var dialog = new

    MessageDialog(this.window, DialogFlags.MODAL, MessageType.INFO, ButtonsType.OK, "The button was clicked!"); dialog.set_title("Information"); dialog.run(); dialog.destroy(); } public static int main(string[] args) { Gtk.init(ref args); var app = new Application(); var button = new Button.with_mnemonic("_Click me"); button.clicked.connect(() => { app.button_clicked(button); }); app.add_to_window(button); app.show(); Gtk.main(); return 0; } 18.messagedialogs.vala
  40. TextView public static int main(string[] args) { Gtk.init(ref args); var

    app = new Application(); var textview = new TextView(); var buffer = textview.get_buffer(); buffer.set_text("Your 1st GtkTextView widget!"); var scrolled_win = new ScrolledWindow(null, null); scrolled_win.add(textview); app.add_to_window(scrolled_win); app.show(); Gtk.main(); return 0; } 19.textview.vala
  41. TextView Properties public static int main(string[] args) { // .

    . . . . var font = FontDescription.from_string("Monospace Bold 10"); var tv = new TextView(); tv.modify_font(font); tv.set_wrap_mode(Gtk.WrapMode.WORD); tv.set_justification(Justification.RIGHT); tv.set_editable(true); tv.set_cursor_visible(true); tv.set_pixels_above_lines(5); tv.set_pixels_below_lines(5); tv.set_pixels_inside_wrap(5); tv.set_left_margin(10); tv.set_right_margin(10); var buffer = tv.get_buffer(); buffer.set_text("This is some text!\nChange me!\nPlease!"); var scrolled_win = new ScrolledWindow(null, null); scrolled_win.set_policy(PolicyType.AUTOMATIC, PolicyType.ALWAYS); scrolled_win.add(tv); app.add_to_window(scrolled_win); app.show(); Gtk.main(); return 0; } 20.properties.vala
  42. Iterators and Marks public static int main(string[] args) { //

    . . . . . var tv = new TextView(); var entry = new Entry(); var insert = new Button.with_label("Insert Text"); var retrieve = new Button.with_label("Get Text"); insert.clicked.connect(() => { insert_text(entry, tv); }); retrieve.clicked.connect(() => { retrieve_text(entry, tv); }); var scrolled_win = new ScrolledWindow(null, null); scrolled_win.add(tv); // . . . . . } public static void insert_text (Entry entry, TextView tv) { var buffer = tv.get_buffer(); var text = entry.get_text(); var mark = buffer.get_insert(); TextIter iter; buffer.get_iter_at_mark(out iter, mark); buffer.insert(iter, text, -1); } 21.iterators.vala public static void retrieve_text (Entry entry, TextView tv) { var buffer = tv.get_buffer(); TextIter start; TextIter end; buffer.get_selection_bounds(out start, out end); var text = buffer.get_text(start, end, false); entry.set_text(text); }
  43. Text Tags public static int main(string[] args) { // .

    . . . . var tv = new TextView(); var buffer = tv.get_buffer(); buffer.create_tag("bold", "weight", Weight.BOLD); buffer.create_tag("italic", "style", Pango.Style.ITALIC); buffer.create_tag("strike", "strikethrough", true); buffer.create_tag("underline", "underline", Underline.SINGLE); var bold = new Button.from_stock(STOCK_BOLD); var italic = new Button.from_stock(STOCK_ITALIC); var underline = new Button.from_stock(STOCK_UNDERLINE); var strike = new Button.from_stock(STOCK_STRIKETHROUGH); var clear = new Button.from_stock(STOCK_CLEAR); bold.set_data("tag", "bold"); italic.set_data("tag", "italic"); underline.set_data("tag", "underline"); strike.set_data("tag", "strike"); bold.clicked.connect(() => { format(bold, tv); }); italic.clicked.connect(() => { format(italic, tv); }); underline.clicked.connect(()=> {format(underline,tv);}); strike.clicked.connect(() => { format(strike, tv); }); clear.clicked.connect(() => { clear_clicked(tv); }); // . . . . . } 22.texttags.vala public static void format(Button button, TextView tv) { var tagname = button.get_data<string>("tag"); var buffer = tv.get_buffer(); TextIter start; TextIter end; buffer.get_selection_bounds(out start, out end); buffer.apply_tag_by_name(tagname, start, end); } public static void clear_clicked(TextView tv) { var buffer = tv.get_buffer(); TextIter start; TextIter end; buffer.get_selection_bounds(out start, out end); buffer.remove_all_tags(start, end); }
  44. Timeout Functions public static bool pulse_progress (ProgressBar progress) { progress.pulse();

    s_count++; return ( s_count < 25 ); } public static int main(string[] args) { Gtk.init(ref args); var app = new Application(); var progress = new ProgressBar(); progress.set_pulse_step(0.1); Timeout.add(100, () => { return pulse_progress(progress); }); app.add_to_window(progress); app.show(); Gtk.main(); return 0; } 23.timeouts.vala
  45. GtkBuilder try { Builder builder = new Builder(); builder.add_from_file("calculator.ui"); builder.connect_signals(null);

    var window = builder.get_object("window") as Window; window.show_all(); Gtk.main(); } catch ( Error error ) { stderr.printf("Error: %s\n", error.message); } valac --pkg gtk+-2.0 --pkg gmodule-2.0 ./*.vala
  46. Reference  Starting GTK+ and Gnome Programming http://cowdinosaur.net/?page_id=6  Gtk+

    HTML backend update http://blogs.gnome.org/alexl/2011/03/15/gtk-html-backend-update/  Foundations of GTK+ Development http://www.amazon.com/Foundations-Development-Experts-Voice-Source/dp/1590597931/  Samples in this tutorial are mostly from this book  GTK+ API Documentation http://www.gtk.org/documentation.html  Valadoc – Vala API Documentation http://live.gnome.org/Vala/Documentation