Blame platform-demos/C/message-board.c.page

Packit 1470ea
Packit 1470ea
Packit 1470ea
      xmlns:its="http://www.w3.org/2005/11/its"
Packit 1470ea
      type="topic"
Packit 1470ea
      id="message-board.c">
Packit 1470ea
Packit 1470ea
  <info>
Packit 1470ea
    <title type="text">Message board (C)</title>
Packit 1470ea
    <link type="guide" xref="c#examples"/>
Packit 1470ea
Packit 1470ea
    <desc>A simple program using WebKitGTK+ and the DOM.</desc>
Packit 1470ea
Packit 1470ea
    <revision pkgversion="0.1" version="0.1" date="2010-12-06" status="draft"/>
Packit 1470ea
    <credit type="author copyright">
Packit 1470ea
      <name>Shaun McCance</name>
Packit 1470ea
      <email its:translate="no">shaunm@gnome.org</email>
Packit 1470ea
      <years>2010</years>
Packit 1470ea
    </credit>
Packit 1470ea
    <credit type="editor">
Packit 1470ea
      <name>Marta Maria Casetti</name>
Packit 1470ea
      <email its:translate="no">mmcasetti@gmail.com</email>
Packit 1470ea
      <years>2013</years>
Packit 1470ea
    </credit>
Packit 1470ea
  </info>
Packit 1470ea
Packit 1470ea
<title>Message board</title>
Packit 1470ea
Packit 1470ea
<synopsis>
Packit 1470ea
  

In this tutorial, you will learn:

Packit 1470ea
  <list style="compact">
Packit 1470ea
    <item>

How to display a web page with WebKit.

</item>
Packit 1470ea
    <item>

How to manipulate the contents of a web page using WebKit's DOM

Packit 1470ea
    functions.

</item>
Packit 1470ea
  </list>
Packit 1470ea
  

This tutorial assumes you are familiar with the C programming language

Packit 1470ea
  and have a basic understanding of GTK+, including how to create and place
Packit 1470ea
  widgets and how to connect callback functions to signals. See 
Packit 1470ea
  xref="image-viewer.c"/> to learn the basics of GTK+.

Packit 1470ea
</synopsis>
Packit 1470ea
Packit 1470ea
<media type="video" mime="video/ogg" src="media/message-board.ogv"/>
Packit 1470ea
Packit 1470ea
<links type="section"/>
Packit 1470ea
Packit 1470ea
<section id="create">
Packit 1470ea
  <title>Create a project in Anjuta</title>
Packit 1470ea
Packit 1470ea
  

The GNOME platform includes WebKitGTK+, built on top of the powerful

Packit 1470ea
  WebKit HTML framework. WebKit is used throughout GNOME, not just to view
Packit 1470ea
  web pages on the Internet, but also to create rich user interfaces that
Packit 1470ea
  can be easily styled with CSS.

Packit 1470ea
Packit 1470ea
  

In this tutorial, you will create a simple message board using WebKit.

Packit 1470ea
  The message board will allow you to enter some text and have it added to a
Packit 1470ea
  list of messages in HTML. Before you begin, you need to set up a project in
Packit 1470ea
  Anjuta.

Packit 1470ea
Packit 1470ea
  <steps>
Packit 1470ea
    <item>

In Anjuta, click <guiseq><gui>File</gui><gui>New</gui>

Packit 1470ea
    <gui>Project</gui></guiseq> to open the new project assistant.

</item>
Packit 1470ea
    <item>

Select <gui>GTK+ (simple)</gui> on the <gui>C</gui> tab,

Packit 1470ea
    and click <gui>Continue</gui>.

</item>
Packit 1470ea
    <item>

Fill out your details on the <gui>Basic information</gui> page.

Packit 1470ea
    Use <input>message-board</input> for the project name.
Packit 1470ea
    Click <gui>Continue</gui>.

</item>
Packit 1470ea
    <item>

Disable the <gui>Use GtkBuilder for user interface</gui> option as

Packit 1470ea
    this tutorial builds the user-interface manually.

Packit 1470ea
    </item>
Packit 1470ea
    <item>

You need to tell Anjuta you're using WebKitGTK+ on this project.

Packit 1470ea
    On the <gui>Project options</gui> page, select <gui>Configure external
Packit 1470ea
    packages</gui>. Click <gui>Continue</gui>. On the <gui>Configure external
Packit 1470ea
    packages</gui> page, check <gui>webkitgtk-3.0</gui>.

</item>
Packit 1470ea
  </steps>
Packit 1470ea
Packit 1470ea
  

After you finish the new project assistant, open the file

Packit 1470ea
  <file>src/main.c</file> from either the <gui>Project</gui> or the
Packit 1470ea
  <gui>File</gui> tab. Anjuta will have filled this in with some basic
Packit 1470ea
  GTK+ code from its templates. Since you are creating a WebKit project,
Packit 1470ea
  you first need to include the WebKit headers. After the line that
Packit 1470ea
  includes gtk/gtk.h, add the following line:

Packit 1470ea
Packit 1470ea
  #include <webkit/webkit.h>
Packit 1470ea
Packit 1470ea
  

Verify that everything works by building what you have so far.

Packit 1470ea
  Click <guiseq><gui>Build</gui><gui>Build Project</gui></guiseq> or
Packit 1470ea
  just press <keyseq><key>Shift</key><key>F7</key></keyseq>. The first
Packit 1470ea
  time you build, you will be asked for some configure options. Just
Packit 1470ea
  accept the defaults and click <gui>Execute</gui>.

Packit 1470ea
Packit 1470ea
  

You should now be able to run the program. Click <guiseq>

Packit 1470ea
  <gui>Run</gui><gui>Execute</gui></guiseq> or just press <key>F3</key>.
Packit 1470ea
  You should see an empty window appear.

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="webview">
Packit 1470ea
  <title>Lay out your window and web view</title>
Packit 1470ea
Packit 1470ea
  

Now that you can show a window, it's time to start working with WebKit.

Packit 1470ea
  For this tutorial, you'll create a text entry and a web view and pack
Packit 1470ea
  them both into a window. Find the function create_window and
Packit 1470ea
  replace it with the following:

Packit 1470ea
Packit 1470ea
Packit 1470ea
static GtkWidget*
Packit 1470ea
create_window (void)
Packit 1470ea
{
Packit 1470ea
    GtkWidget *window, *box, *scroll, *view, *entry;
Packit 1470ea
Packit 1470ea
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
Packit 1470ea
    gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
Packit 1470ea
    gtk_window_set_title (GTK_WINDOW (window), "Message Board");
Packit 1470ea
    g_signal_connect (window, "delete-event", G_CALLBACK (gtk_main_quit), NULL);
Packit 1470ea
Packit 1470ea
    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
Packit 1470ea
    gtk_container_set_border_width (GTK_CONTAINER (box), 6);
Packit 1470ea
    gtk_container_add (GTK_CONTAINER (window), box);
Packit 1470ea
Packit 1470ea
    entry = gtk_entry_new ();
Packit 1470ea
    gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 0);
Packit 1470ea
Packit 1470ea
    scroll = gtk_scrolled_window_new (NULL, NULL);
Packit 1470ea
    g_object_set (scroll, "shadow-type", GTK_SHADOW_IN, NULL);
Packit 1470ea
    gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);
Packit 1470ea
Packit 1470ea
    view = webkit_web_view_new ();
Packit 1470ea
    gtk_container_add (GTK_CONTAINER (scroll), view);
Packit 1470ea
    webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
Packit 1470ea
                                 "<html><body></body></html>",
Packit 1470ea
                                 "text/html",
Packit 1470ea
                                 "UTF-8",
Packit 1470ea
                                 NULL);
Packit 1470ea
Packit 1470ea
    gtk_widget_show_all (GTK_WIDGET (box));
Packit 1470ea
    return window;
Packit 1470ea
}
Packit 1470ea
]]>
Packit 1470ea
Packit 1470ea
  

You first create a GtkWindow object and set its title and

Packit 1470ea
  default size. You also connect the gtk_main_quit function to the
Packit 1470ea
  delete-event signal. The delete-event signal is
Packit 1470ea
  emitted when the window is closed. The gtk_main_quit function is
Packit 1470ea
  part of GTK, and it quits the application.

Packit 1470ea
Packit 1470ea
  

You then create a vertical box and add it to the window. A window can only

Packit 1470ea
  hold a single child widget, so you need to use a box to add multiple widgets.
Packit 1470ea
  The second argument to gtk_box_new sets the amount of padding
Packit 1470ea
  (in pixels) between each child, and the next line puts a six-pixel border
Packit 1470ea
  around the entire thing.

Packit 1470ea
Packit 1470ea
  

You next create a GtkEntry object and pack it into the box.

Packit 1470ea
  The third and fourth arguments to gtk_box_pack_start specify that
Packit 1470ea
  the entry shouldn't take up any extra space the box has available. The fourth
Packit 1470ea
  argument is the amount of padding you want around the entry. In this case,
Packit 1470ea
  you set the padding to zero, because you're allowing the box to handle all
Packit 1470ea
  the padding.

Packit 1470ea
Packit 1470ea
  

Before you add a web view, you have to create a scrolled window to put it

Packit 1470ea
  inside of. The scrolled window will place scrollbars on the right and bottom
Packit 1470ea
  when necessary, and prevent your web view from filling your entire screen.
Packit 1470ea
  This time, you pass TRUE and TRUE to
Packit 1470ea
  gtk_box_pack_start to allow the scrolled window (and thus, the
Packit 1470ea
  web view) to use any extra space available in the box.

Packit 1470ea
Packit 1470ea
  

Finally, you create a WebKitWebView and add it to the scrolled

Packit 1470ea
  window. Then load a very basic HTML page into the web view by calling
Packit 1470ea
  webkit_web_view_load_string with the following arguments:

Packit 1470ea
Packit 1470ea
  <terms>
Packit 1470ea
    <item>
Packit 1470ea
      <title>WEBKIT_WEB_VIEW (view)</title>
Packit 1470ea
      

The view itself. Because view is typed as a

Packit 1470ea
      GtkWidget*, you have to use WEBKIT_WEB_VIEW
Packit 1470ea
      to safely cast the object.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
      <title>"<html><body></body></html>"</title>
Packit 1470ea
      

The simplest HTML file you could possibly write.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
      <title>"text/html"</title>
Packit 1470ea
      

The MIME type of the content you provided. In this case, you're

Packit 1470ea
      using plain HTML.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
      <title>"UTF-8"</title>
Packit 1470ea
      

The character encoding of the content you provided. Although you only

Packit 1470ea
      used ASCII characters, it's a good idea to specify UTF-8. UTF-8 is used
Packit 1470ea
      as the default encoding throughout the GNOME platform.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
      <title>NULL</title>
Packit 1470ea
      

The base URI. You don't need it in this simple example, but you might

Packit 1470ea
      want to provide a <sys>file:</sys> URI if you add images or other features
Packit 1470ea
      where you want to use relative URI references.

Packit 1470ea
    </item>
Packit 1470ea
  </terms>
Packit 1470ea
Packit 1470ea
  <note style="sidebar">
Packit 1470ea
    

Every time you add a widget, you have to call gtk_widget_show

Packit 1470ea
    on it for it to be visible. If you call gtk_widget_show_all on
Packit 1470ea
    a container widget like a GtkBox, GTK+ will automatically show
Packit 1470ea
    all the widgets inside the container, to any depth. Sometimes you don't
Packit 1470ea
    want to call gtk_widget_show_all, such as when you want to
Packit 1470ea
    dynamically hide and show some widgets in response to events.

Packit 1470ea
  </note>
Packit 1470ea
Packit 1470ea
  

Finally, you have to call gtk_widget_show_all on the box.

Packit 1470ea
  Otherwise, none of the widgets you created will be visible. (The window is
Packit 1470ea
  shown in the main function with gtk_widget_show.)

Packit 1470ea
Packit 1470ea
  

Build and run the message board again. You should see a window with a text

Packit 1470ea
  entry and a web view. It doesn't do anything yet because the text entry and
Packit 1470ea
  the web view don't know anything about each other.

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="signals">
Packit 1470ea
  <title>Hook up signals</title>
Packit 1470ea
Packit 1470ea
  

Now you want to make the message board actually do something

Packit 1470ea
  when you enter text into the text entry. To do this, connect a callback
Packit 1470ea
  function to the activate signal of entry. GTK+
Packit 1470ea
  emits the activate signal whenever the user presses
Packit 1470ea
  <key>Enter</key> in the entry. Add the following into create_window,
Packit 1470ea
  anywhere after both entry and view have been defined:

Packit 1470ea
Packit 1470ea
Packit 1470ea
g_signal_connect (entry, "activate", G_CALLBACK (entry_activate_cb), view);
Packit 1470ea
]]>
Packit 1470ea
Packit 1470ea
  

You then have to actually define entry_activate_cb. Define

Packit 1470ea
  it as follows, anywhere above create_window:

Packit 1470ea
Packit 1470ea
Packit 1470ea
static void
Packit 1470ea
entry_activate_cb (GtkEntry *entry, WebKitWebView *view)
Packit 1470ea
{
Packit 1470ea
    WebKitDOMDocument *document;
Packit 1470ea
    WebKitDOMElement *body, *div;
Packit 1470ea
Packit 1470ea
    document = webkit_web_view_get_dom_document (view);
Packit 1470ea
    body = webkit_dom_document_query_selector (document, "body", NULL);
Packit 1470ea
    div = webkit_dom_document_create_element (document, "div", NULL);
Packit 1470ea
    webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (div),
Packit 1470ea
                                      gtk_entry_get_text (entry),
Packit 1470ea
                                      NULL);
Packit 1470ea
    webkit_dom_node_append_child (WEBKIT_DOM_NODE (body),
Packit 1470ea
                                  WEBKIT_DOM_NODE (div),
Packit 1470ea
                                  NULL);
Packit 1470ea
    gtk_entry_set_text (entry, "");
Packit 1470ea
}
Packit 1470ea
]]>
Packit 1470ea
Packit 1470ea
  

The first thing you do is get a WebKitDOMDocument object

Packit 1470ea
  that represents the HTML document displayed in view. The DOM
Packit 1470ea
  classes and methods in WebKit allow you to inspect and manipulate the HTML
Packit 1470ea
  document, and work very similarly to the DOM APIs you might already know
Packit 1470ea
  from JavaScript.

Packit 1470ea
Packit 1470ea
  

Once you have the document, you want to get the body element

Packit 1470ea
  so that you can add div elements to it. The
Packit 1470ea
  webkit_dom_document_query_selector function lets you find an
Packit 1470ea
  element in the document using CSS selectors. This keeps you from having to
Packit 1470ea
  write tedious loops to traverse the document.

Packit 1470ea
Packit 1470ea
  <comment>
Packit 1470ea
    shaunm
Packit 1470ea
    

FIXME: Is this true? Does query_selector take CSS, CSSish, or what?

Packit 1470ea
  </comment>
Packit 1470ea
Packit 1470ea
  

Next, you create a new div element to hold the message. Every

Packit 1470ea
  element you create has to be attached to a document, so the function to create
Packit 1470ea
  an element takes the WebKitDOMDocument as its first arguments.
Packit 1470ea
  You then set the text content of the element to the contents of the text entry.
Packit 1470ea
  Because gtk_entry_get_text returns a const gchar*,
Packit 1470ea
  you don't have to free the result.

Packit 1470ea
Packit 1470ea
  <comment>
Packit 1470ea
    shaunm
Packit 1470ea
    

Not passing the GError**, but we should give it a quick mention and

Packit 1470ea
    link to somewhere that explains how GError-handling works.

Packit 1470ea
  </comment>
Packit 1470ea
Packit 1470ea
  

Finally, you append the new div element to the body and

Packit 1470ea
  clear out the text entry so you can type something new. Build and run the
Packit 1470ea
  program again and test it for yourself.

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
Packit 1470ea
<section id="css">
Packit 1470ea
  <title>Make it look better with CSS</title>
Packit 1470ea
Packit 1470ea
  

At this point, your program is completely functional, but not very pretty.

Packit 1470ea
  You can style the message display with CSS, just like you can with any other
Packit 1470ea
  HTML page. There are many ways you could attach some CSS to the page: You
Packit 1470ea
  could add it in the initial HTML document. You could inline it in the
Packit 1470ea
  style attribute of the div elements. You could
Packit 1470ea
  even programmatically construct it using the DOM APIs.

Packit 1470ea
Packit 1470ea
  

In this tutorial, you'll attach the CSS using the user-stylesheet-uri

Packit 1470ea
  property of the WebKitWebSetting object attached to your web view.
Packit 1470ea
  In a more complete application, you would want to save and load your HTML file.
Packit 1470ea
  Keeping the style information outside the actual HTML means that you can change
Packit 1470ea
  the styling completely within your application, without having to change users'
Packit 1470ea
  files. You would normally just install a file along with your application, but
Packit 1470ea
  just to keep everything in one file for this demo, we'll use a trick called a
Packit 1470ea
  data URI. First, define the CSS as a static string near the top of your file.

Packit 1470ea
Packit 1470ea
Packit 1470ea
static const guchar CSS[] =
Packit 1470ea
"body { margin: 0; padding: 0; }\n"
Packit 1470ea
"div { "
Packit 1470ea
" -webkit-border-radius: 2px;"
Packit 1470ea
" background: -webkit-gradient(linear, 0% 100%, 0% 0%,"
Packit 1470ea
" from(#f1f1f1), to(white));"
Packit 1470ea
" border: solid 1px #c6c6c6;"
Packit 1470ea
" -webkit-box-shadow: 0px 0px 2px #c6c6c6;"
Packit 1470ea
" margin: 12px; padding: 6px;"
Packit 1470ea
"}";
Packit 1470ea
]]>
Packit 1470ea
Packit 1470ea
  

All you have in this example are div elements inside a

Packit 1470ea
  body element. If you created more complicated HTML, you could
Packit 1470ea
  use whatever CSS is necessary. In fact, if you're comfortable with CSS, you
Packit 1470ea
  should trying changing this to something you like better.

Packit 1470ea
Packit 1470ea
  

To apply the CSS, you set the user-stylesheet-uri in the

Packit 1470ea
  create_window function, anywhere after view has
Packit 1470ea
  already been defined.

Packit 1470ea
Packit 1470ea
  <comment>shaunm

g_base64_encode has bad args

</comment>
Packit 1470ea
Packit 1470ea
Packit 1470ea
tmp = g_base64_encode (CSS, strlen((gchar *) CSS));
Packit 1470ea
css = g_strconcat ("data:text/css;charset=utf-8;base64,",
Packit 1470ea
                   tmp, NULL);
Packit 1470ea
g_object_set (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view)),
Packit 1470ea
              "user-stylesheet-uri", css, NULL);
Packit 1470ea
g_free (css);
Packit 1470ea
g_free (tmp);
Packit 1470ea
]]>
Packit 1470ea
Packit 1470ea
  

Also, make sure to add variable declarations for tmp

Packit 1470ea
  and css to the top of create_window.

Packit 1470ea
Packit 1470ea
Packit 1470ea
gchar *tmp, *css;
Packit 1470ea
Packit 1470ea
Packit 1470ea
 

A data URI starts with <sys>data:</sys> and some information about

Packit 1470ea
 the content type and how the data is encoded. The actual data follows after
Packit 1470ea
 a comma, in this case encoded in Base64. Unlike other URI schemes like
Packit 1470ea
 <sys>http:</sys>, <sys>ftp:</sys>, and <sys>file:</sys>, the <sys>data:</sys>
Packit 1470ea
 URI scheme doesn't specify where to find a file to load. Rather, it gives
Packit 1470ea
 the entire contents of the file.

Packit 1470ea
Packit 1470ea
 

The code above first encodes your CSS definitions in Base64, then

Packit 1470ea
 combines that with a fixed string to create a data URI. The
Packit 1470ea
 g_strconcat function can take any number of string arguments
Packit 1470ea
 and concatenate them all together, so you have to pass NULL
Packit 1470ea
 as the final argument so it knows when to stop. And don't forget to free
Packit 1470ea
 those temporary strings after you set the stylesheet property.

Packit 1470ea
Packit 1470ea
 

Build and run the program again. It should now work exactly the same

Packit 1470ea
 as at the end of the last section, except the messages will be nicely
Packit 1470ea
 styled with a border and a subtle background gradient.

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="more">
Packit 1470ea
  <title>Learn more</title>
Packit 1470ea
Packit 1470ea
  

This tutorial showed you how to create a basic application using GTK+

Packit 1470ea
  and WebKit, including showing a document and manipulating its contents.
Packit 1470ea
  To create a real application, you probably want to do a little bit more.
Packit 1470ea
  Try adding features on your own. Here are a few ideas:

Packit 1470ea
Packit 1470ea
  <list>
Packit 1470ea
    <item>

If you're comfortable with CSS, try changing the style of the

Packit 1470ea
    message display. CSS is easy to get started with, but increasingly more
Packit 1470ea
    powerful. There is a wealth of CSS tutorials on the Internet, and just
Packit 1470ea
    about everything you can do on the web, you can do in this
Packit 1470ea
    application.

</item>
Packit 1470ea
Packit 1470ea
    <item>

Right now, you lose all your messages whenever you close the

Packit 1470ea
    message board. Try saving the HTML contents after each post, and loading
Packit 1470ea
    the saved file (if it exists) on startup.

Packit 1470ea
    <comment>
Packit 1470ea
      shaunm

Link to method to get HTML from DOM and to

Packit 1470ea
      GIO APIs.

Packit 1470ea
    </comment></item>
Packit 1470ea
Packit 1470ea
    <item>

If you keep your messages around for a long time, you'll start

Packit 1470ea
    wondering when you posted them. Add a timestamp to each message when it's
Packit 1470ea
    posted. You'll probably want to create some additional child div
Packit 1470ea
    elements with different classes that you can style in the CSS.

Packit 1470ea
    <comment>shaunm

Link to strftime or something

</comment>
Packit 1470ea
    </item>
Packit 1470ea
Packit 1470ea
    <item>

This program keeps messages around forever. Think about ways

Packit 1470ea
    you could allow the user to delete messages. Perhaps you want messages
Packit 1470ea
    to disappear automatically after they're too old, or after there are a
Packit 1470ea
    certain number of messages before them. Or you could add a link in each
Packit 1470ea
    message to delete it. You could even override the context menu when you
Packit 1470ea
    right-click on a message. These features involve exploring WebKit's DOM
Packit 1470ea
    API more.

</item>
Packit 1470ea
  </list>
Packit 1470ea
</section>
Packit 1470ea
</page>