[recipes] Move about dialog to its own files



commit 713735f581deb5180fa5ba079b05650c3e57df51
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Feb 9 20:34:10 2017 -0500

    Move about dialog to its own files
    
    This cleans up our application class and lets us use
    the modal stacking for the about dialog as well.

 src/Makefile.am       |    2 +
 src/gr-about-dialog.c |  664 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/gr-about-dialog.h |   33 +++
 src/gr-app.c          |  614 +---------------------------------------------
 src/gr-window.c       |   22 ++
 src/gr-window.h       |    1 +
 6 files changed, 723 insertions(+), 613 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index c4225ff..679f91c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,8 @@ endif
 
 recipes_SOURCES = \
        main.c                  \
+       gr-about-dialog.h       \
+       gr-about-dialog.c       \
        gr-app.h                \
        gr-app.c                \
        gr-category-tile.h      \
diff --git a/src/gr-about-dialog.c b/src/gr-about-dialog.c
new file mode 100644
index 0000000..218a930
--- /dev/null
+++ b/src/gr-about-dialog.c
@@ -0,0 +1,664 @@
+/* gr-about-dialog.c:
+ *
+ * Copyright (C) 2017 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include "gr-about-dialog.h"
+#include "gr-utils.h"
+
+struct _GrAboutDialog
+{
+        GtkAboutDialog parent_instance;
+};
+
+G_DEFINE_TYPE (GrAboutDialog, gr_about_dialog, GTK_TYPE_ABOUT_DIALOG)
+
+static void
+gr_about_dialog_finalize (GObject *object)
+{
+        G_OBJECT_CLASS (gr_about_dialog_parent_class)->finalize (object);
+}
+
+static void
+gr_about_dialog_init (GrAboutDialog *self)
+{
+}
+
+static void
+gr_about_dialog_class_init (GrAboutDialogClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gr_about_dialog_finalize;
+}
+
+static GtkWidget *
+find_child_with_name (GtkWidget  *parent,
+                      const char *name)
+{
+        GList *children, *l;
+        GtkWidget *result = NULL;
+
+        children = gtk_container_get_children (GTK_CONTAINER (parent));
+        for (l = children; l; l = l->next) {
+                GtkWidget *child = l->data;
+
+                if (g_strcmp0 (gtk_buildable_get_name (GTK_BUILDABLE (child)), name) == 0) {
+                        result = child;
+                        break;
+                }
+        }
+        g_list_free (children);
+
+        if (result == NULL)
+                g_warning ("Didn't find %s in GtkAboutDialog\n", name);
+        return result;
+}
+
+static void
+builder_info (GtkButton *button,
+              GtkWidget *about)
+{
+        const char *uri = "http://wiki.gnome.org/Apps/Builder";;
+        g_autoptr(GError) error = NULL;
+
+        gtk_show_uri_on_window (GTK_WINDOW (about), uri, GDK_CURRENT_TIME, &error);
+        if (error)
+                g_warning ("Unable to show '%s': %s", uri, error->message);
+}
+
+static void
+add_built_logo (GrAboutDialog *about)
+{
+        GtkWidget *content;
+        GtkWidget *box;
+        GtkWidget *stack;
+        GtkWidget *page_vbox;
+        GtkWidget *license_label;
+        GtkWidget *copyright_label;
+        GtkWidget *button;
+        GtkWidget *image;
+        g_autoptr(GdkPixbuf) pixbuf = NULL;
+        g_autoptr(GtkCssProvider) provider = NULL;
+        g_autofree char *css = NULL;
+        const char *path;
+
+        content = gtk_dialog_get_content_area (GTK_DIALOG (about));
+        box = find_child_with_name (content, "box");
+        stack = find_child_with_name (box, "stack");
+        page_vbox = find_child_with_name (stack, "page_vbox");
+        license_label = find_child_with_name (page_vbox, "license_label");
+        copyright_label = find_child_with_name (page_vbox, "copyright_label");
+
+        box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+        gtk_widget_show (box);
+        button = gtk_button_new ();
+        g_signal_connect (button, "clicked", G_CALLBACK (builder_info), about);
+        gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
+        gtk_box_pack_start (GTK_BOX (box), button, FALSE, TRUE, 0);
+        gtk_widget_set_valign (button, GTK_ALIGN_END);
+        gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+        gtk_widget_set_tooltip_text (button, _("Learn more about Builder"));
+        gtk_widget_show (button);
+
+        image = g_object_new (g_type_from_name ("GtkIcon"), NULL);
+        path = "/org/gnome/Recipes/built-with-builder-symbolic.symbolic.png";
+        pixbuf = gdk_pixbuf_new_from_resource (path, NULL);
+        css = g_strdup_printf (".built-with-builder {\n"
+              "  -gtk-icon-source: -gtk-recolor(url('resource://%s'));\n"
+              "  min-width: %dpx;\n"
+              "  min-height: %dpx;\n"
+              "}", path, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf));
+        gtk_style_context_add_class (gtk_widget_get_style_context (image), "built-with-builder");
+        provider = gtk_css_provider_new ();
+        gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), 
900);
+        gtk_css_provider_load_from_data (provider, css, -1, NULL);
+
+        gtk_widget_show (image);
+        gtk_container_add (GTK_CONTAINER (button), image);
+
+        g_object_ref (license_label);
+        g_object_ref (copyright_label);
+
+        gtk_container_remove (GTK_CONTAINER (page_vbox), license_label);
+        gtk_container_remove (GTK_CONTAINER (page_vbox), copyright_label);
+
+        gtk_box_pack_start (GTK_BOX (box), license_label, TRUE, TRUE, 0);
+        gtk_label_set_justify (GTK_LABEL (license_label), GTK_JUSTIFY_LEFT);
+        gtk_widget_set_valign (license_label, GTK_ALIGN_END);
+
+        gtk_container_add (GTK_CONTAINER (page_vbox), copyright_label);
+        gtk_container_add (GTK_CONTAINER (page_vbox), box);
+
+        g_object_unref (license_label);
+        g_object_unref (copyright_label);
+}
+
+static void
+get_flatpak_information (char **flatpak_version,
+                         char **app_id,
+                         char **app_arch,
+                         char **app_branch,
+                         char **app_commit,
+                         char **runtime_id,
+                         char **runtime_arch,
+                         char **runtime_branch,
+                         char **runtime_commit)
+{
+        g_autoptr(GKeyFile) keyfile = NULL;
+        g_autoptr(GError) error = NULL;
+        g_autofree char *path = NULL;
+        char *p;
+        g_auto(GStrv) strv = NULL;
+
+        *flatpak_version = NULL;
+        *app_id = NULL;
+        *app_arch = NULL;
+        *app_branch = NULL;
+        *app_commit = NULL;
+        *runtime_id = NULL;
+        *runtime_arch = NULL;
+        *runtime_branch = NULL;
+        *runtime_commit = NULL;
+
+        keyfile = g_key_file_new ();
+        if (!g_key_file_load_from_file (keyfile, "/.flatpak-info", G_KEY_FILE_NONE, &error)) {
+                goto error;
+        }
+
+        *flatpak_version = g_key_file_get_string (keyfile, "Instance", "flatpak-version", &error);
+        if (*flatpak_version == NULL)
+                goto error;
+
+        path = g_key_file_get_string (keyfile, "Instance", "app-path", &error);
+        if (path == NULL)
+                goto error;
+
+        p = strstr (path, "app/");
+        if (p == NULL)
+                goto error;
+
+        p += strlen ("app/");
+        strv = g_strsplit (p, "/", -1);
+
+        if (g_strv_length (strv) < 4)
+                goto error;
+
+        *app_id = g_strdup (strv[0]);
+        *app_arch = g_strdup (strv[1]);
+        *app_branch = g_strdup (strv[2]);
+        *app_commit = g_strdup (strv[3]);
+
+        path = g_key_file_get_string (keyfile, "Instance", "runtime-path", &error);
+        if (path == NULL)
+                goto error;
+
+        p = strstr (path, "runtime/");
+        if (p == NULL)
+                goto error;
+
+        p += strlen ("runtime/");
+        strv = g_strsplit (p, "/", -1);
+
+        if (g_strv_length (strv) < 4)
+                goto error;
+
+        *runtime_id = g_strdup (strv[0]);
+        *runtime_arch = g_strdup (strv[1]);
+        *runtime_branch = g_strdup (strv[2]);
+        *runtime_commit = g_strdup (strv[3]);
+
+        return;
+
+error:
+        g_message ("Failed to load runtime information: %s", error ? error->message : "");
+
+        if (!*flatpak_version)
+                *flatpak_version = g_strdup (_("Unknown"));
+        if (!*app_id)
+                *app_id = g_strdup (_("Unknown"));
+        if (!*app_arch)
+                *app_arch = g_strdup (_("Unknown"));
+        if (!*app_branch)
+                *app_branch = g_strdup (_("Unknown"));
+        if (!*app_commit)
+                *app_commit = g_strdup (_("Unknown"));
+        if (!*runtime_id)
+                *runtime_id = g_strdup (_("Unknown"));
+        if (!*runtime_arch)
+                *runtime_arch = g_strdup (_("Unknown"));
+        if (!*runtime_branch)
+                *runtime_branch = g_strdup (_("Unknown"));
+        if (!*runtime_commit)
+                *runtime_commit = g_strdup (_("Unknown"));
+}
+
+static void
+text_buffer_append (GtkTextBuffer *buffer,
+                    const char    *text)
+{
+        GtkTextIter iter;
+
+        gtk_text_buffer_get_end_iter (buffer, &iter);
+        gtk_text_buffer_insert (buffer, &iter, text, -1);
+}
+
+static void
+text_buffer_append_printf (GtkTextBuffer *buffer,
+                           const char    *format,
+                           ...)
+{
+        va_list args;
+        char *buf;
+        int len;
+
+        va_start (args, format);
+
+        len = g_vasprintf (&buf, format, args);
+        if (len >= 0) {
+                text_buffer_append (buffer, buf);
+                g_free (buf);
+        }
+
+        va_end (args);
+}
+
+static void
+text_buffer_append_link (GtkTextView *view,
+                         GtkTextBuffer *buffer,
+                         const char    *name,
+                         const char    *uri)
+{
+        GdkRGBA color;
+        GtkTextTag *tag;
+        GtkTextIter iter;
+        GtkStateFlags state = gtk_widget_get_state_flags (GTK_WIDGET (view));
+        GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (view));
+
+        gtk_style_context_get_color (context, state | GTK_STATE_FLAG_LINK, &color);
+
+        tag = gtk_text_buffer_create_tag (buffer, NULL,
+                                          "foreground-rgba", &color,
+                                          "underline", PANGO_UNDERLINE_SINGLE,
+                                          NULL);
+        g_object_set_data_full (G_OBJECT (tag), "uri", g_strdup (uri), g_free);
+        gtk_text_buffer_get_end_iter (buffer, &iter);
+        gtk_text_buffer_insert_with_tags (buffer, &iter, name, -1, tag, NULL);
+}
+
+
+static void
+populate_system_tab (GtkTextView *view)
+{
+        GtkTextBuffer *buffer;
+        PangoTabArray *tabs;
+        GtkTextIter start, end;
+
+        tabs = pango_tab_array_new (3, TRUE);
+        pango_tab_array_set_tab (tabs, 0, PANGO_TAB_LEFT, 20);
+        pango_tab_array_set_tab (tabs, 1, PANGO_TAB_LEFT, 150);
+        pango_tab_array_set_tab (tabs, 2, PANGO_TAB_LEFT, 230);
+        gtk_text_view_set_tabs (view, tabs);
+        pango_tab_array_free (tabs);
+
+        buffer = gtk_text_view_get_buffer (view);
+
+        if (in_flatpak_sandbox ()) {
+                g_autofree char *flatpak_version = NULL;
+                g_autofree char *app_id = NULL;
+                g_autofree char *app_arch = NULL;
+                g_autofree char *app_branch = NULL;
+                g_autofree char *app_commit = NULL;
+                g_autofree char *runtime_id = NULL;
+                g_autofree char *runtime_arch = NULL;
+                g_autofree char *runtime_branch = NULL;
+                g_autofree char *runtime_commit = NULL;
+
+                get_flatpak_information (&flatpak_version,
+                                         &app_id, &app_arch, &app_branch, &app_commit,
+                                         &runtime_id, &runtime_arch, &runtime_branch, &runtime_commit);
+
+                text_buffer_append (buffer, _("Flatpak"));
+                text_buffer_append (buffer, "\n");
+
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Version"), 
flatpak_version);
+
+                text_buffer_append (buffer, "\n");
+                text_buffer_append (buffer, _("Application"));
+                text_buffer_append (buffer, "\n");
+
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "ID"), app_id);
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Architecture"), 
app_arch);
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Branch"), 
app_branch);
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Commit"), 
app_commit);
+
+                text_buffer_append (buffer, "\n");
+                text_buffer_append (buffer, _("Runtime"));
+                text_buffer_append (buffer, "\n");
+
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "ID"), runtime_id);
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Architecture"), 
runtime_arch);
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Branch"), 
runtime_branch);
+                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Commit"), 
runtime_commit);
+
+                text_buffer_append (buffer, "\n");
+                text_buffer_append (buffer, _("Bundled libraries"));
+                text_buffer_append (buffer, "\n");
+
+#if ENABLE_AUTOAR
+                text_buffer_append_printf (buffer, "\tgnome-autoar\t%s\t", AUTOAR_VERSION);
+                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
+                text_buffer_append (buffer, "\n");
+#endif
+#if ENABLE_GSPELL
+                text_buffer_append_printf (buffer, "\tgspell\t%s\t", GSPELL_VERSION);
+                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
+                text_buffer_append (buffer, "\n");
+#endif
+                text_buffer_append_printf (buffer, "\tlibgd\t%s\t", LIBGD_INFO);
+                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
+                text_buffer_append (buffer, "\n");
+                text_buffer_append_printf (buffer, "\tlibglnx\t%s\t", LIBGLNX_INFO);
+                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
+                text_buffer_append (buffer, "\n");
+        }
+        else {
+                text_buffer_append (buffer, _("System libraries"));
+                text_buffer_append (buffer, "\n");
+                text_buffer_append_printf (buffer, "\tGLib\t%d.%d.%d\n",
+                                           glib_major_version,
+                                           glib_minor_version,
+                                           glib_micro_version);
+                text_buffer_append_printf (buffer, "\tGTK+\t%d.%d.%d\n",
+                                           gtk_get_major_version (),
+                                           gtk_get_minor_version (),
+                                           gtk_get_micro_version ());
+#if ENABLE_AUTOAR
+                text_buffer_append_printf (buffer, "\tgnome-autoar\t%s\n", AUTOAR_VERSION);
+#endif
+#if ENABLE_GSPELL
+                text_buffer_append_printf (buffer, "\tgspell\t%s\n", GSPELL_VERSION);
+#endif
+
+                text_buffer_append (buffer, "\n");
+                text_buffer_append (buffer, _("Bundled libraries"));
+                text_buffer_append (buffer, "\n");
+
+                text_buffer_append_printf (buffer, "\tlibgd\t%s\t", LIBGD_INFO);
+                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
+                text_buffer_append (buffer, "\n");
+                text_buffer_append_printf (buffer, "\tlibglnx\t%s\t", LIBGLNX_INFO);
+                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
+       }
+
+        gtk_text_buffer_create_tag (buffer, "smaller", "scale", PANGO_SCALE_SMALL, NULL);
+        gtk_text_buffer_get_bounds (buffer, &start, &end);
+        gtk_text_buffer_apply_tag_by_name (buffer, "smaller", &start, &end);
+}
+
+static void
+follow_if_link (GrAboutDialog *about,
+                GtkTextView   *text_view,
+                GtkTextIter   *iter)
+{
+        GSList *tags = NULL, *tagp = NULL;
+        gchar *uri = NULL;
+
+        tags = gtk_text_iter_get_tags (iter);
+        for (tagp = tags; tagp != NULL && !uri; tagp = tagp->next) {
+                GtkTextTag *tag = tagp->data;
+
+                uri = g_object_get_data (G_OBJECT (tag), "uri");
+                if (uri)
+                        gtk_show_uri_on_window (GTK_WINDOW (about), uri, GDK_CURRENT_TIME, NULL);
+        }
+
+        g_slist_free (tags);
+}
+
+static gboolean
+text_view_key_press_event (GtkWidget     *text_view,
+                           GdkEventKey   *event,
+                           GrAboutDialog *about)
+{
+        GtkTextIter iter;
+        GtkTextBuffer *buffer;
+
+        switch (event->keyval)
+        {
+        case GDK_KEY_Return:
+        case GDK_KEY_ISO_Enter:
+        case GDK_KEY_KP_Enter:
+                buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+                gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+                                                  gtk_text_buffer_get_insert (buffer));
+                follow_if_link (about, GTK_TEXT_VIEW (text_view), &iter);
+                break;
+
+        default:
+                break;
+        }
+
+        return FALSE;
+}
+
+static gboolean
+text_view_event_after (GtkWidget     *text_view,
+                       GdkEvent      *event,
+                       GrAboutDialog *about)
+{
+        GtkTextIter start, end, iter;
+        GtkTextBuffer *buffer;
+        GdkEventButton *button_event;
+        gint x, y;
+
+        if (event->type != GDK_BUTTON_RELEASE)
+                return FALSE;
+
+        button_event = (GdkEventButton *)event;
+
+        if (button_event->button != GDK_BUTTON_PRIMARY)
+                return FALSE;
+
+        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+
+        /* we shouldn't follow a link if the user has selected something */
+        gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+        if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
+                return FALSE;
+
+        gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+                                               GTK_TEXT_WINDOW_WIDGET,
+                                               button_event->x, button_event->y, &x, &y);
+
+        gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
+
+        follow_if_link (about, GTK_TEXT_VIEW (text_view), &iter);
+
+        return FALSE;
+}
+
+static void
+set_cursor_if_appropriate (GrAboutDialog *about,
+                           GtkTextView   *text_view,
+                           GdkDevice     *device,
+                           gint           x,
+                           gint           y)
+{
+        GSList *tags = NULL, *tagp = NULL;
+        GtkTextIter iter;
+        gboolean hovering_over_link = FALSE;
+        gboolean was_hovering = FALSE;
+
+        gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+
+        tags = gtk_text_iter_get_tags (&iter);
+        for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
+                GtkTextTag *tag = tagp->data;
+                gchar *uri = g_object_get_data (G_OBJECT (tag), "uri");
+
+                if (uri != NULL) {
+                        hovering_over_link = TRUE;
+                        break;
+                }
+        }
+
+        was_hovering = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (about), "hovering-over-link"));
+        if (hovering_over_link != was_hovering) {
+                GdkCursor *cursor;
+
+                g_object_set_data (G_OBJECT (about), "hovering-over-link", GINT_TO_POINTER 
(hovering_over_link));
+
+                if (hovering_over_link)
+                        cursor = GDK_CURSOR (g_object_get_data (G_OBJECT (about), "pointer-cursor"));
+                else
+                        cursor = GDK_CURSOR (g_object_get_data (G_OBJECT (about), "text-cursor"));
+
+                gdk_window_set_device_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), 
device, cursor);
+        }
+
+        g_slist_free (tags);
+}
+
+static gboolean
+text_view_motion_notify_event (GtkWidget      *text_view,
+                               GdkEventMotion *event,
+                               GrAboutDialog  *about)
+{
+        gint x, y;
+
+        gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+                                               GTK_TEXT_WINDOW_WIDGET,
+                                               event->x, event->y, &x, &y);
+
+        set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), event->device, x, y);
+
+        gdk_event_request_motions (event);
+
+        return FALSE;
+}
+
+static void
+add_system_tab (GrAboutDialog *about)
+{
+        GtkWidget *content;
+        GtkWidget *box;
+        GtkWidget *stack;
+        GtkWidget *sw;
+        GtkWidget *view;
+        GdkCursor *cursor;
+
+        content = gtk_dialog_get_content_area (GTK_DIALOG (about));
+        box = find_child_with_name (content, "box");
+        stack = find_child_with_name (box, "stack");
+
+        sw = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                        GTK_POLICY_AUTOMATIC,
+                                        GTK_POLICY_AUTOMATIC);
+        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+                                             GTK_SHADOW_IN);
+        gtk_widget_show (sw);
+        view = gtk_text_view_new ();
+        gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
+        gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 10);
+        gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view), 10);
+        gtk_text_view_set_top_margin (GTK_TEXT_VIEW (view), 10);
+        gtk_text_view_set_bottom_margin (GTK_TEXT_VIEW (view), 10);
+
+        cursor = gdk_cursor_new_from_name (gdk_display_get_default (), "pointer");
+        g_object_set_data_full (G_OBJECT (about), "pointer-cursor", cursor, g_object_unref);
+
+        cursor = gdk_cursor_new_from_name (gdk_display_get_default (), "text");
+        g_object_set_data_full (G_OBJECT (about), "text-cursor", cursor, g_object_unref);
+
+        g_signal_connect (view, "event-after", G_CALLBACK (text_view_event_after), about);
+        g_signal_connect (view, "key-press-event", G_CALLBACK (text_view_key_press_event), about);
+        g_signal_connect (view, "motion-notify-event", G_CALLBACK (text_view_motion_notify_event), about);
+        gtk_widget_show (view);
+        gtk_container_add (GTK_CONTAINER (sw), view);
+
+        gtk_stack_add_titled (GTK_STACK (stack), sw, "system", _("System"));
+
+        populate_system_tab (GTK_TEXT_VIEW (view));
+}
+
+GrAboutDialog *
+gr_about_dialog_new (void)
+{
+        GrAboutDialog *about;
+        const char *authors[] = {
+                "Emel Elvin Yıldız",
+                "Matthias Clasen",
+                "Jakub Steiner",
+                "Christian Hergert",
+                "Matthew Leeds",
+                "Mohammed Sadiq",
+                "Sam Hewitt",
+                NULL
+        };
+        const char *recipe_authors[] = {
+                "Ray Strode",
+                "Bastian Ilsø",
+                "Frederik Fyksen",
+                "Matthias Clasen",
+                "Allan Day",
+                "Erusan",
+                "Link Dupont",
+                "Tuomas Kuosmanen",
+                "Matthew Leeds",
+                NULL
+        };
+        g_autoptr(GdkPixbuf) logo = NULL;
+
+        logo = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                         "org.gnome.Recipes",
+                                         256,
+                                         GTK_ICON_LOOKUP_FORCE_SIZE,
+                                         NULL);
+
+        about = g_object_new (GR_TYPE_ABOUT_DIALOG,
+                              "program-name", _("GNOME Recipes"),
+#if MICRO_VERSION % 2 == 1
+                              "version", COMMIT_ID,
+#else
+                              "version", PACKAGE_VERSION,
+#endif
+                              "copyright", "© 2016, 2017 Matthias Clasen",
+                              "license-type", GTK_LICENSE_GPL_3_0,
+                              "comments", _("GNOME loves to cook"),
+                              "authors", authors,
+                              "translator-credits", _("translator-credits"),
+                              "logo", logo,
+                              "website", "https://wiki.gnome.org/Apps/Recipes";,
+                              "website-label", _("Learn more about GNOME Recipes"),
+                              NULL);
+
+        gtk_about_dialog_add_credit_section (GTK_ABOUT_DIALOG (about),
+                                             _("Recipes by"), recipe_authors);
+        add_built_logo (about);
+        add_system_tab (about);
+
+        return about;
+}
diff --git a/src/gr-about-dialog.h b/src/gr-about-dialog.h
new file mode 100644
index 0000000..30074e0
--- /dev/null
+++ b/src/gr-about-dialog.h
@@ -0,0 +1,33 @@
+/* gr-about-dialog.h:
+ *
+ * Copyright (C) 2017 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GR_TYPE_ABOUT_DIALOG (gr_about_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (GrAboutDialog, gr_about_dialog, GR, ABOUT_DIALOG, GtkAboutDialog)
+
+GrAboutDialog *gr_about_dialog_new (void);
+
+G_END_DECLS
diff --git a/src/gr-app.c b/src/gr-app.c
index aac8a1a..0b6871e 100644
--- a/src/gr-app.c
+++ b/src/gr-app.c
@@ -75,630 +75,18 @@ chef_information_activated (GSimpleAction *action,
         GtkWindow *win;
 
         win = gtk_application_get_active_window (GTK_APPLICATION (app));
-
         gr_window_show_my_chef_information (GR_WINDOW (win));
 }
 
-static GtkWidget *
-find_child_with_name (GtkWidget  *parent,
-                      const char *name)
-{
-        GList *children, *l;
-        GtkWidget *result = NULL;
-
-        children = gtk_container_get_children (GTK_CONTAINER (parent));
-        for (l = children; l; l = l->next) {
-                GtkWidget *child = l->data;
-
-                if (g_strcmp0 (gtk_buildable_get_name (GTK_BUILDABLE (child)), name) == 0) {
-                        result = child;
-                        break;
-                }
-        }
-        g_list_free (children);
-
-        if (result == NULL)
-                g_warning ("Didn't find %s in GtkAboutDialog\n", name);
-        return result;
-}
-
-static void
-builder_info (GtkButton *button, GtkWidget *about)
-{
-        const char *uri = "http://wiki.gnome.org/Apps/Builder";;
-        g_autoptr(GError) error = NULL;
-
-        gtk_show_uri_on_window (GTK_WINDOW (about), uri, GDK_CURRENT_TIME, &error);
-        if (error)
-                g_warning ("Unable to show '%s': %s", uri, error->message);
-}
-
-static void
-add_built_logo (GtkAboutDialog *about)
-{
-        GtkWidget *content;
-        GtkWidget *box;
-        GtkWidget *stack;
-        GtkWidget *page_vbox;
-        GtkWidget *license_label;
-        GtkWidget *copyright_label;
-        GtkWidget *button;
-        GtkWidget *image;
-        g_autoptr(GdkPixbuf) pixbuf = NULL;
-        g_autoptr(GtkCssProvider) provider = NULL;
-        g_autofree char *css = NULL;
-        const char *path;
-
-        content = gtk_dialog_get_content_area (GTK_DIALOG (about));
-        box = find_child_with_name (content, "box");
-        stack = find_child_with_name (box, "stack");
-        page_vbox = find_child_with_name (stack, "page_vbox");
-        license_label = find_child_with_name (page_vbox, "license_label");
-        copyright_label = find_child_with_name (page_vbox, "copyright_label");
-
-        box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
-        gtk_widget_show (box);
-        button = gtk_button_new ();
-        g_signal_connect (button, "clicked", G_CALLBACK (builder_info), about);
-        gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
-        gtk_box_pack_start (GTK_BOX (box), button, FALSE, TRUE, 0);
-        gtk_widget_set_valign (button, GTK_ALIGN_END);
-        gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
-        gtk_widget_set_tooltip_text (button, _("Learn more about Builder"));
-        gtk_widget_show (button);
-
-        image = g_object_new (g_type_from_name ("GtkIcon"), NULL);
-        path = "/org/gnome/Recipes/built-with-builder-symbolic.symbolic.png";
-        pixbuf = gdk_pixbuf_new_from_resource (path, NULL);
-        css = g_strdup_printf (".built-with-builder {\n"
-              "  -gtk-icon-source: -gtk-recolor(url('resource://%s'));\n"
-              "  min-width: %dpx;\n"
-              "  min-height: %dpx;\n"
-              "}", path, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf));
-        gtk_style_context_add_class (gtk_widget_get_style_context (image), "built-with-builder");
-        provider = gtk_css_provider_new ();
-        gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), 
900);
-        gtk_css_provider_load_from_data (provider, css, -1, NULL);
-
-        gtk_widget_show (image);
-        gtk_container_add (GTK_CONTAINER (button), image);
-
-        g_object_ref (license_label);
-        g_object_ref (copyright_label);
-
-        gtk_container_remove (GTK_CONTAINER (page_vbox), license_label);
-        gtk_container_remove (GTK_CONTAINER (page_vbox), copyright_label);
-
-        gtk_box_pack_start (GTK_BOX (box), license_label, TRUE, TRUE, 0);
-        gtk_label_set_justify (GTK_LABEL (license_label), GTK_JUSTIFY_LEFT);
-        gtk_widget_set_valign (license_label, GTK_ALIGN_END);
-
-        gtk_container_add (GTK_CONTAINER (page_vbox), copyright_label);
-        gtk_container_add (GTK_CONTAINER (page_vbox), box);
-
-        g_object_unref (license_label);
-        g_object_unref (copyright_label);
-}
-
-static void
-get_flatpak_information (char **flatpak_version,
-                         char **app_id,
-                         char **app_arch,
-                         char **app_branch,
-                         char **app_commit,
-                         char **runtime_id,
-                         char **runtime_arch,
-                         char **runtime_branch,
-                         char **runtime_commit)
-{
-        g_autoptr(GKeyFile) keyfile = NULL;
-        g_autoptr(GError) error = NULL;
-        g_autofree char *path = NULL;
-        char *p;
-        g_auto(GStrv) strv = NULL;
-
-        *flatpak_version = NULL;
-        *app_id = NULL;
-        *app_arch = NULL;
-        *app_branch = NULL;
-        *app_commit = NULL;
-        *runtime_id = NULL;
-        *runtime_arch = NULL;
-        *runtime_branch = NULL;
-        *runtime_commit = NULL;
-
-        keyfile = g_key_file_new ();
-        if (!g_key_file_load_from_file (keyfile, "/.flatpak-info", G_KEY_FILE_NONE, &error)) {
-                goto error;
-        }
-
-        *flatpak_version = g_key_file_get_string (keyfile, "Instance", "flatpak-version", &error);
-        if (*flatpak_version == NULL)
-                goto error;
-
-        path = g_key_file_get_string (keyfile, "Instance", "app-path", &error);
-        if (path == NULL)
-                goto error;
-
-        p = strstr (path, "app/");
-        if (p == NULL)
-                goto error;
-
-        p += strlen ("app/");
-        strv = g_strsplit (p, "/", -1);
-
-        if (g_strv_length (strv) < 4)
-                goto error;
-
-        *app_id = g_strdup (strv[0]);
-        *app_arch = g_strdup (strv[1]);
-        *app_branch = g_strdup (strv[2]);
-        *app_commit = g_strdup (strv[3]);
-
-        path = g_key_file_get_string (keyfile, "Instance", "runtime-path", &error);
-        if (path == NULL)
-                goto error;
-
-        p = strstr (path, "runtime/");
-        if (p == NULL)
-                goto error;
-
-        p += strlen ("runtime/");
-        strv = g_strsplit (p, "/", -1);
-
-        if (g_strv_length (strv) < 4)
-                goto error;
-
-        *runtime_id = g_strdup (strv[0]);
-        *runtime_arch = g_strdup (strv[1]);
-        *runtime_branch = g_strdup (strv[2]);
-        *runtime_commit = g_strdup (strv[3]);
-
-        return;
-
-error:
-        g_message ("Failed to load runtime information: %s", error ? error->message : "");
-
-        if (!*flatpak_version)
-                *flatpak_version = g_strdup (_("Unknown"));
-        if (!*app_id)
-                *app_id = g_strdup (_("Unknown"));
-        if (!*app_arch)
-                *app_arch = g_strdup (_("Unknown"));
-        if (!*app_branch)
-                *app_branch = g_strdup (_("Unknown"));
-        if (!*app_commit)
-                *app_commit = g_strdup (_("Unknown"));
-        if (!*runtime_id)
-                *runtime_id = g_strdup (_("Unknown"));
-        if (!*runtime_arch)
-                *runtime_arch = g_strdup (_("Unknown"));
-        if (!*runtime_branch)
-                *runtime_branch = g_strdup (_("Unknown"));
-        if (!*runtime_commit)
-                *runtime_commit = g_strdup (_("Unknown"));
-}
-
-static void
-text_buffer_append (GtkTextBuffer *buffer,
-                    const char    *text)
-{
-        GtkTextIter iter;
-
-        gtk_text_buffer_get_end_iter (buffer, &iter);
-        gtk_text_buffer_insert (buffer, &iter, text, -1);
-}
-
-static void
-text_buffer_append_printf (GtkTextBuffer *buffer,
-                           const char    *format,
-                           ...)
-{
-        va_list args;
-        char *buf;
-        int len;
-
-        va_start (args, format);
-
-        len = g_vasprintf (&buf, format, args);
-        if (len >= 0) {
-                text_buffer_append (buffer, buf);
-                g_free (buf);
-        }
-
-        va_end (args);
-}
-
-static void
-text_buffer_append_link (GtkTextView *view,
-                         GtkTextBuffer *buffer,
-                         const char    *name,
-                         const char    *uri)
-{
-        GdkRGBA color;
-        GtkTextTag *tag;
-        GtkTextIter iter;
-        GtkStateFlags state = gtk_widget_get_state_flags (GTK_WIDGET (view));
-        GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (view));
-
-        gtk_style_context_get_color (context, state | GTK_STATE_FLAG_LINK, &color);
-
-        tag = gtk_text_buffer_create_tag (buffer, NULL,
-                                          "foreground-rgba", &color,
-                                          "underline", PANGO_UNDERLINE_SINGLE,
-                                          NULL);
-        g_object_set_data_full (G_OBJECT (tag), "uri", g_strdup (uri), g_free);
-        gtk_text_buffer_get_end_iter (buffer, &iter);
-        gtk_text_buffer_insert_with_tags (buffer, &iter, name, -1, tag, NULL);
-}
-
-static void
-populate_system_tab (GtkTextView *view)
-{
-        GtkTextBuffer *buffer;
-        PangoTabArray *tabs;
-        GtkTextIter start, end;
-
-        tabs = pango_tab_array_new (3, TRUE);
-        pango_tab_array_set_tab (tabs, 0, PANGO_TAB_LEFT, 20);
-        pango_tab_array_set_tab (tabs, 1, PANGO_TAB_LEFT, 150);
-        pango_tab_array_set_tab (tabs, 2, PANGO_TAB_LEFT, 230);
-        gtk_text_view_set_tabs (view, tabs);
-        pango_tab_array_free (tabs);
-
-        buffer = gtk_text_view_get_buffer (view);
-
-        if (in_flatpak_sandbox ()) {
-                g_autofree char *flatpak_version = NULL;
-                g_autofree char *app_id = NULL;
-                g_autofree char *app_arch = NULL;
-                g_autofree char *app_branch = NULL;
-                g_autofree char *app_commit = NULL;
-                g_autofree char *runtime_id = NULL;
-                g_autofree char *runtime_arch = NULL;
-                g_autofree char *runtime_branch = NULL;
-                g_autofree char *runtime_commit = NULL;
-
-                get_flatpak_information (&flatpak_version,
-                                         &app_id, &app_arch, &app_branch, &app_commit,
-                                         &runtime_id, &runtime_arch, &runtime_branch, &runtime_commit);
-
-                text_buffer_append (buffer, _("Flatpak"));
-                text_buffer_append (buffer, "\n");
-
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Version"), 
flatpak_version);
-
-                text_buffer_append (buffer, "\n");
-                text_buffer_append (buffer, _("Application"));
-                text_buffer_append (buffer, "\n");
-
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "ID"), app_id);
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Architecture"), 
app_arch);
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Branch"), 
app_branch);
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Commit"), 
app_commit);
-
-                text_buffer_append (buffer, "\n");
-                text_buffer_append (buffer, _("Runtime"));
-                text_buffer_append (buffer, "\n");
-
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "ID"), runtime_id);
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Architecture"), 
runtime_arch);
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Branch"), 
runtime_branch);
-                text_buffer_append_printf (buffer, "\t%s\t%s\n", C_("Flatpak metadata", "Commit"), 
runtime_commit);
-
-                text_buffer_append (buffer, "\n");
-                text_buffer_append (buffer, _("Bundled libraries"));
-                text_buffer_append (buffer, "\n");
-
-#if ENABLE_AUTOAR
-                text_buffer_append_printf (buffer, "\tgnome-autoar\t%s\t", AUTOAR_VERSION);
-                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
-                text_buffer_append (buffer, "\n");
-#endif
-#if ENABLE_GSPELL
-                text_buffer_append_printf (buffer, "\tgspell\t%s\t", GSPELL_VERSION);
-                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
-                text_buffer_append (buffer, "\n");
-#endif
-                text_buffer_append_printf (buffer, "\tlibgd\t%s\t", LIBGD_INFO);
-                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
-                text_buffer_append (buffer, "\n");
-                text_buffer_append_printf (buffer, "\tlibglnx\t%s\t", LIBGLNX_INFO);
-                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
-                text_buffer_append (buffer, "\n");
-        }
-        else {
-                text_buffer_append (buffer, _("System libraries"));
-                text_buffer_append (buffer, "\n");
-                text_buffer_append_printf (buffer, "\tGLib\t%d.%d.%d\n",
-                                           glib_major_version,
-                                           glib_minor_version,
-                                           glib_micro_version);
-                text_buffer_append_printf (buffer, "\tGTK+\t%d.%d.%d\n",
-                                           gtk_get_major_version (),
-                                           gtk_get_minor_version (),
-                                           gtk_get_micro_version ());
-#if ENABLE_AUTOAR
-                text_buffer_append_printf (buffer, "\tgnome-autoar\t%s\n", AUTOAR_VERSION);
-#endif
-#if ENABLE_GSPELL
-                text_buffer_append_printf (buffer, "\tgspell\t%s\n", GSPELL_VERSION);
-#endif
-
-                text_buffer_append (buffer, "\n");
-                text_buffer_append (buffer, _("Bundled libraries"));
-                text_buffer_append (buffer, "\n");
-
-                text_buffer_append_printf (buffer, "\tlibgd\t%s\t", LIBGD_INFO);
-                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
-                text_buffer_append (buffer, "\n");
-                text_buffer_append_printf (buffer, "\tlibglnx\t%s\t", LIBGLNX_INFO);
-                text_buffer_append_link (view, buffer, "LGPLv2", 
"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html";);
-       }
-
-        gtk_text_buffer_create_tag (buffer, "smaller", "scale", PANGO_SCALE_SMALL, NULL);
-        gtk_text_buffer_get_bounds (buffer, &start, &end);
-        gtk_text_buffer_apply_tag_by_name (buffer, "smaller", &start, &end);
-}
-
-static void
-follow_if_link (GtkAboutDialog *about,
-                GtkTextView    *text_view,
-                GtkTextIter    *iter)
-{
-  GSList *tags = NULL, *tagp = NULL;
-  gchar *uri = NULL;
-
-  tags = gtk_text_iter_get_tags (iter);
-  for (tagp = tags; tagp != NULL && !uri; tagp = tagp->next)
-    {
-      GtkTextTag *tag = tagp->data;
-
-      uri = g_object_get_data (G_OBJECT (tag), "uri");
-      if (uri)
-        gtk_show_uri_on_window (GTK_WINDOW (about), uri, GDK_CURRENT_TIME, NULL);
-    }
-
-  g_slist_free (tags);
-}
-
-static gboolean
-text_view_key_press_event (GtkWidget      *text_view,
-                           GdkEventKey    *event,
-                           GtkAboutDialog *about)
-{
-  GtkTextIter iter;
-  GtkTextBuffer *buffer;
-
-  switch (event->keyval)
-    {
-      case GDK_KEY_Return:
-      case GDK_KEY_ISO_Enter:
-      case GDK_KEY_KP_Enter:
-        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
-        gtk_text_buffer_get_iter_at_mark (buffer, &iter,
-                                          gtk_text_buffer_get_insert (buffer));
-        follow_if_link (about, GTK_TEXT_VIEW (text_view), &iter);
-        break;
-
-      default:
-        break;
-    }
-
-  return FALSE;
-}
-
-static gboolean
-text_view_event_after (GtkWidget      *text_view,
-                       GdkEvent       *event,
-                       GtkAboutDialog *about)
-{
-  GtkTextIter start, end, iter;
-  GtkTextBuffer *buffer;
-  GdkEventButton *button_event;
-  gint x, y;
-
-  if (event->type != GDK_BUTTON_RELEASE)
-    return FALSE;
-
-  button_event = (GdkEventButton *)event;
-
-  if (button_event->button != GDK_BUTTON_PRIMARY)
-    return FALSE;
-
-  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
-
-  /* we shouldn't follow a link if the user has selected something */
-  gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
-  if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
-    return FALSE;
-
-  gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
-                                         GTK_TEXT_WINDOW_WIDGET,
-                                         button_event->x, button_event->y, &x, &y);
-
-  gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
-
-  follow_if_link (about, GTK_TEXT_VIEW (text_view), &iter);
-
-  return FALSE;
-}
-
-static void
-set_cursor_if_appropriate (GtkAboutDialog *about,
-                           GtkTextView    *text_view,
-                           GdkDevice      *device,
-                           gint            x,
-                           gint            y)
-{
-  GSList *tags = NULL, *tagp = NULL;
-  GtkTextIter iter;
-  gboolean hovering_over_link = FALSE;
-  gboolean was_hovering = FALSE;
-
-  gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
-
-  tags = gtk_text_iter_get_tags (&iter);
-  for (tagp = tags;  tagp != NULL;  tagp = tagp->next)
-    {
-      GtkTextTag *tag = tagp->data;
-      gchar *uri = g_object_get_data (G_OBJECT (tag), "uri");
-
-      if (uri != NULL)
-        {
-          hovering_over_link = TRUE;
-          break;
-        }
-    }
-
-  was_hovering = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (about), "hovering-over-link"));
-  if (hovering_over_link != was_hovering)
-    {
-      GdkCursor *cursor;
-
-      g_object_set_data (G_OBJECT (about), "hovering-over-link", GINT_TO_POINTER (hovering_over_link));
-
-      if (hovering_over_link)
-        cursor = GDK_CURSOR (g_object_get_data (G_OBJECT (about), "pointer-cursor"));
-      else
-        cursor = GDK_CURSOR (g_object_get_data (G_OBJECT (about), "text-cursor"));
-
-      gdk_window_set_device_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), device, 
cursor);
-    }
-
-  g_slist_free (tags);
-}
-
-static gboolean
-text_view_motion_notify_event (GtkWidget      *text_view,
-                               GdkEventMotion *event,
-                               GtkAboutDialog *about)
-{
-  gint x, y;
-
-  gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
-                                         GTK_TEXT_WINDOW_WIDGET,
-                                         event->x, event->y, &x, &y);
-
-  set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), event->device, x, y);
-
-  gdk_event_request_motions (event);
-
-  return FALSE;
-}
-
-static void
-add_system_tab (GtkAboutDialog *about)
-{
-        GtkWidget *content;
-        GtkWidget *box;
-        GtkWidget *stack;
-        GtkWidget *sw;
-        GtkWidget *view;
-        GdkCursor *cursor;
-
-        content = gtk_dialog_get_content_area (GTK_DIALOG (about));
-        box = find_child_with_name (content, "box");
-        stack = find_child_with_name (box, "stack");
-
-        sw = gtk_scrolled_window_new (NULL, NULL);
-        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
-                                        GTK_POLICY_AUTOMATIC,
-                                        GTK_POLICY_AUTOMATIC);
-        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
-                                             GTK_SHADOW_IN);
-        gtk_widget_show (sw);
-        view = gtk_text_view_new ();
-        gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
-        gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 10);
-        gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view), 10);
-        gtk_text_view_set_top_margin (GTK_TEXT_VIEW (view), 10);
-        gtk_text_view_set_bottom_margin (GTK_TEXT_VIEW (view), 10);
-
-        cursor = gdk_cursor_new_from_name (gdk_display_get_default (), "pointer");
-        g_object_set_data_full (G_OBJECT (about), "pointer-cursor", cursor, g_object_unref);
-
-        cursor = gdk_cursor_new_from_name (gdk_display_get_default (), "text");
-        g_object_set_data_full (G_OBJECT (about), "text-cursor", cursor, g_object_unref);
-
-        g_signal_connect (view, "event-after", G_CALLBACK (text_view_event_after), about);
-        g_signal_connect (view, "key-press-event", G_CALLBACK (text_view_key_press_event), about);
-        g_signal_connect (view, "motion-notify-event", G_CALLBACK (text_view_motion_notify_event), about);
-        gtk_widget_show (view);
-        gtk_container_add (GTK_CONTAINER (sw), view);
-
-        gtk_stack_add_titled (GTK_STACK (stack), sw, "system", _("System"));
-
-        populate_system_tab (GTK_TEXT_VIEW (view));
-}
-
 static void
 about_activated (GSimpleAction *action,
                  GVariant      *parameter,
                  gpointer       app)
 {
         GtkWindow *win;
-        const char *authors[] = {
-                "Emel Elvin Yıldız",
-                "Matthias Clasen",
-                "Jakub Steiner",
-                "Christian Hergert",
-                "Matthew Leeds",
-                "Mohammed Sadiq",
-                "Sam Hewitt",
-                NULL
-        };
-        const char *recipe_authors[] = {
-                "Ray Strode",
-                "Bastian Ilsø",
-                "Frederik Fyksen",
-                "Matthias Clasen",
-                "Allan Day",
-                "Erusan",
-                "Link Dupont",
-                "Tuomas Kuosmanen",
-                "Matthew Leeds",
-                NULL
-        };
-
-        g_autoptr(GdkPixbuf) logo = NULL;
-        static gboolean first_time = TRUE;
-
-        logo = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
-                                         "org.gnome.Recipes",
-                                         256,
-                                         GTK_ICON_LOOKUP_FORCE_SIZE,
-                                         NULL);
 
         win = gtk_application_get_active_window (GTK_APPLICATION (app));
-        gtk_show_about_dialog (GTK_WINDOW (win),
-                               "program-name", _("GNOME Recipes"),
-#if MICRO_VERSION % 2 == 1
-                               "version", COMMIT_ID,
-#else
-                               "version", PACKAGE_VERSION,
-#endif
-                               "copyright", "© 2016 Matthias Clasen",
-                               "license-type", GTK_LICENSE_GPL_3_0,
-                               "comments", _("GNOME loves to cook"),
-                               "authors", authors,
-                               "translator-credits", _("translator-credits"),
-                               "logo", logo,
-                               "website", "https://wiki.gnome.org/Apps/Recipes";,
-                               "website-label", _("Learn more about GNOME Recipes"),
-                               NULL);
-
-        if (first_time) {
-                GtkAboutDialog *dialog;
-
-                first_time = FALSE;
-
-                dialog = GTK_ABOUT_DIALOG (g_object_get_data (G_OBJECT (win), "gtk-about-dialog"));
-                gtk_about_dialog_add_credit_section (dialog, _("Recipes by"), recipe_authors);
-                add_built_logo (dialog);
-                add_system_tab (dialog);
-        }
-
+        gr_window_show_about_dialog (GR_WINDOW (win));
 }
 
 static void
diff --git a/src/gr-window.c b/src/gr-window.c
index 10fda78..8434be0 100644
--- a/src/gr-window.c
+++ b/src/gr-window.c
@@ -38,6 +38,7 @@
 #include "gr-image-page.h"
 #include "gr-query-editor.h"
 #include "gr-recipe-importer.h"
+#include "gr-about-dialog.h"
 
 
 struct _GrWindow
@@ -75,6 +76,7 @@ struct _GrWindow
         GrRecipeImporter *importer;
 
         GtkWidget *chef_dialog;
+        GtkWidget *about_dialog;
 
         GList *dialogs;
 
@@ -935,3 +937,23 @@ gr_window_present_dialog (GrWindow  *window,
 
         gtk_window_present (dialog);
 }
+
+static void
+about_response (GtkWidget *dialog,
+                int        response,
+                GrWindow  *window)
+{
+        gtk_widget_destroy (dialog);
+        window->about_dialog = NULL;
+}
+
+void
+gr_window_show_about_dialog (GrWindow *window)
+{
+        if (window->about_dialog)
+                return;
+
+        window->about_dialog = (GtkWidget *)gr_about_dialog_new ();
+        g_signal_connect (window->about_dialog, "response", G_CALLBACK (about_response), window);
+        gr_window_present_dialog (window, GTK_WINDOW (window->about_dialog));
+}
diff --git a/src/gr-window.h b/src/gr-window.h
index 631852d..ae7c4e4 100644
--- a/src/gr-window.h
+++ b/src/gr-window.h
@@ -75,5 +75,6 @@ void            gr_window_show_image                 (GrWindow   *window,
 void            gr_window_offer_shopping             (GrWindow   *window);
 
 void            gr_window_show_my_chef_information   (GrWindow   *window);
+void            gr_window_show_about_dialog          (GrWindow   *window);
 
 G_END_DECLS


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]