[gnome-software] Break out the sources dialog as a separate object



commit 37feb53766cc0616f0c179c2c4b7e911bacc3e50
Author: Kalev Lember <kalevlember gmail com>
Date:   Sat Mar 15 10:54:03 2014 +0100

    Break out the sources dialog as a separate object
    
    At the same time, move to a separate GtkBuilder template.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=726413

 po/POTFILES.in                   |    2 +
 src/Makefile.am                  |    5 +-
 src/gnome-software.gresource.xml |    1 +
 src/gnome-software.ui            |  483 -------------------------------------
 src/gs-shell.c                   |  375 +-----------------------------
 src/gs-sources-dialog.c          |  428 +++++++++++++++++++++++++++++++++
 src/gs-sources-dialog.h          |   59 +++++
 src/gs-sources-dialog.ui         |  485 ++++++++++++++++++++++++++++++++++++++
 8 files changed, 986 insertions(+), 852 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9c5e3d8..10b376e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -24,6 +24,8 @@ src/gs-shell-installed.c
 src/gs-shell-overview.c
 src/gs-shell-search.c
 src/gs-shell-updates.c
+src/gs-sources-dialog.c
+[type: gettext/glade]src/gs-sources-dialog.ui
 src/gs-update-dialog.c
 [type: gettext/glade]src/gs-update-dialog.ui
 src/gs-update-monitor.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 42ec26f..ed7f738 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,7 +33,8 @@ UI_FILES =                                            \
        category-tile.ui                                \
        feature-tile.ui                                 \
        gnome-software.ui                               \
-       gs-history-dialog.ui                    \
+       gs-history-dialog.ui                            \
+       gs-sources-dialog.ui                            \
        gs-star-widget.ui                               \
        gs-update-dialog.ui                             \
        screenshot-image.ui                             \
@@ -132,6 +133,8 @@ gnome_software_SOURCES =                            \
        gs-shell-updates.h                              \
        gs-shell-search.c                               \
        gs-shell-search.h                               \
+       gs-sources-dialog.c                             \
+       gs-sources-dialog.h                             \
        gs-update-dialog.c                              \
        gs-update-dialog.h                              \
        gs-update-monitor.c                             \
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 06f8f97..a851a6d 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -11,6 +11,7 @@
   <file preprocess="xml-stripblanks">app-folder-dialog.ui</file>
   <file preprocess="xml-stripblanks">screenshot-image.ui</file>
   <file preprocess="xml-stripblanks">gs-history-dialog.ui</file>
+  <file preprocess="xml-stripblanks">gs-sources-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-star-widget.ui</file>
   <file preprocess="xml-stripblanks">gs-update-dialog.ui</file>
   <file preprocess="xml-stripblanks">org.freedesktop.PackageKit.xml</file>
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index 4fc826f..bca78fb 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -2070,489 +2070,6 @@
       <widget name="button_folder_fake"/>
     </widgets>
   </object>
-
-  <object class="GtkWindow" id="window_sources">
-    <property name="can_focus">False</property>
-    <property name="modal">True</property>
-    <property name="destroy_with_parent">True</property>
-    <property name="type_hint">dialog</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="transient_for">window_software</property>
-    <property name="default-width">460</property>
-    <property name="default-height">500</property>
-    <property name="title" translatable="yes">Software Sources</property>
-    <child>
-      <object class="GtkBox" id="box_sources_main">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="orientation">vertical</property>
-        <child>
-          <object class="GtkHeaderBar" id="header_sources">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="show_close_button">True</property>
-
-            <child>
-              <object class="GtkButton" id="button_sources_back">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">False</property>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="button_sources_back_accessible">
-                    <property name="accessible-name" translatable="yes">Go back</property>
-                  </object>
-                </child>
-                <style>
-                  <class name="image-button"/>
-                </style>
-                <child>
-                  <object class="GtkImage" id="image_sources_back">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="icon_name">go-previous-symbolic</property>
-                    <property name="icon_size">1</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child type="title">
-              <object class="GtkLabel" id="label_sources_header">
-                <property name="can_focus">False</property>
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Software Sources</property>
-                <property name="selectable">False</property>
-                <style>
-                  <class name="title"/>
-                </style>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkStack" id="stack_sources">
-            <property name="visible">True</property>
-            <child>
-              <object class="GtkSpinner" id="spinner_sources">
-                <property name="visible">True</property>
-                <property name="width_request">128</property>
-                <property name="height_request">128</property>
-                <property name="halign">center</property>
-                <property name="valign">center</property>
-                <property name="hexpand">True</property>
-                <property name="vexpand">True</property>
-              </object>
-              <packing>
-                <property name="name">sources-waiting</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkBox" id="box_sources_empty">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="orientation">vertical</property>
-                <child>
-                  <object class="GtkLabel" id="label_sources_empty">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="justify">center</property>
-                    <property name="label" translatable="yes">No sources found.</property>
-                    <property name="halign">center</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="name">sources-empty</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkBox" id="box_sources1">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="margin_left">16</property>
-                <property name="margin_right">16</property>
-                <property name="margin_top">16</property>
-                <property name="margin_bottom">32</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">4</property>
-                <child>
-                  <object class="GtkLabel" id="label_sources1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Software sources give you access to additional 
software.</property>
-                    <property name="wrap">True</property>
-                    <property name="max_width_chars">30</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="label_sources3">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Removing a source will also remove any 
software you have installed from it.</property>
-                    <property name="wrap">True</property>
-                    <property name="max_width_chars">30</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkScrolledWindow" id="scrolledwindow_sources">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">never</property>
-                    <property name="vscrollbar_policy">automatic</property>
-                    <property name="shadow_type">none</property>
-                    <property name="margin_top">9</property>
-                    <child>
-                      <object class="GtkFrame" id="frame_sources">
-                        <property name="visible">True</property>
-                        <property name="shadow_type">in</property>
-                        <property name="halign">fill</property>
-                        <property name="valign">start</property>
-                        <child>
-                          <object class="GtkListBox" id="listbox_sources">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="selection_mode">none</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="name">sources</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkBox" id="box_sources2">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="margin_left">16</property>
-                <property name="margin_right">16</property>
-                <property name="margin_top">16</property>
-                <property name="margin_bottom">16</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">4</property>
-                <child>
-                  <object class="GtkGrid" id="grid_sources_noresults">
-                    <property name="visible">True</property>
-                    <property name="hexpand">True</property>
-                    <property name="vexpand">True</property>
-                    <property name="halign">center</property>
-                    <property name="valign">center</property>
-                    <property name="row-spacing">12</property>
-                    <property name="column-spacing">12</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                    <child>
-                      <object class="GtkImage" id="image_sources_noresults">
-                        <property name="visible">True</property>
-                        <property name="icon_name">gnome-software-symbolic</property>
-                        <property name="pixel-size">64</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="left-attach">0</property>
-                        <property name="top-attach">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_noresults">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">No software installed from this 
source</property>
-                        <property name="halign">center</property>
-                        <property name="valign">center</property>
-                        <property name="justify">center</property>
-                        <property name="wrap">True</property>
-                        <property name="max_width_chars">25</property>
-                        <attributes>
-                          <attribute name="scale" value="1.2"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="left-attach">0</property>
-                        <property name="top-attach">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="label_sources2">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="xalign">0.5</property>
-                    <property name="label" translatable="yes">Installed from this Source</property>
-                    <property name="wrap">True</property>
-                    <property name="max_width_chars">30</property>
-                    <attributes>
-                      <attribute name="weight" value="bold"/>
-                    </attributes>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkScrolledWindow" id="scrolledwindow_sources_apps">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">never</property>
-                    <property name="vscrollbar_policy">automatic</property>
-                    <property name="shadow_type">none</property>
-                    <child>
-                      <object class="GtkFrame" id="frame_sources_apps">
-                        <property name="visible">True</property>
-                        <property name="shadow_type">in</property>
-                        <property name="halign">fill</property>
-                        <property name="valign">start</property>
-                        <child>
-                          <object class="GtkListBox" id="listbox_sources_apps">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="selection_mode">none</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="label_sources_details">
-                    <property name="can_focus">False</property>
-                    <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Source Details</property>
-                    <property name="wrap">True</property>
-                    <property name="max_width_chars">30</property>
-                    <attributes>
-                      <attribute name="weight" value="bold"/>
-                    </attributes>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkGrid" id="grid_sources_details">
-                    <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="valign">start</property>
-                    <property name="row_spacing">3</property>
-                    <property name="column_spacing">24</property>
-                    <property name="row_homogeneous">True</property>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_header_version">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Version</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="left_attach">0</property>
-                        <property name="top_attach">0</property>
-                        <property name="width">1</property>
-                        <property name="height">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_version">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label">0.12.3</property>
-                      </object>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="top_attach">0</property>
-                        <property name="width">1</property>
-                        <property name="height">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_header_lastchecked">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Last Checked</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="left_attach">0</property>
-                        <property name="top_attach">2</property>
-                        <property name="width">1</property>
-                        <property name="height">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_header_added">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Added</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="left_attach">0</property>
-                        <property name="top_attach">1</property>
-                        <property name="width">1</property>
-                        <property name="height">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_header_website">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Website</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="left_attach">0</property>
-                        <property name="top_attach">3</property>
-                        <property name="width">1</property>
-                        <property name="height">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_added">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label">May 12, 2012</property>
-                      </object>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="top_attach">1</property>
-                        <property name="width">1</property>
-                        <property name="height">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_website">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label">superrepo.com</property>
-                      </object>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="top_attach">3</property>
-                        <property name="width">1</property>
-                        <property name="height">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label_sources_lastchecked">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">January 30, 2014</property>
-                      </object>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="top_attach">2</property>
-                        <property name="width">1</property>
-                        <property name="height">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">3</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkButton" id="button_sources_remove">
-                    <property name="label" translatable="yes">Remove Source</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
-                    <property name="margin_top">12</property>
-                    <style>
-                      <class name="destructive-action"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="position">4</property>
-                    <property name="pack_type">end</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="name">source-details</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </object>
-    </child>
-  </object>
   <object class="GtkSizeGroup" id="sizegroup_details_title">
     <property name="ignore_hidden">True</property>
     <widgets>
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 5d39cbb..a906951 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -33,6 +33,7 @@
 #include "gs-shell-overview.h"
 #include "gs-shell-updates.h"
 #include "gs-shell-category.h"
+#include "gs-sources-dialog.h"
 
 static const gchar *page_name[] = {
        "overview",
@@ -454,190 +455,6 @@ window_key_press_event (GtkWidget *win, GdkEventKey *event, GsShell *shell)
 }
 
 /**
- * gs_shell_sources_list_header_func
- **/
-static void
-gs_shell_sources_list_header_func (GtkListBoxRow *row,
-                                  GtkListBoxRow *before,
-                                  gpointer user_data)
-{
-       GtkWidget *header = NULL;
-       if (before != NULL)
-               header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-       gtk_list_box_row_set_header (row, header);
-}
-
-/**
- * gs_shell_sources_list_sort_func:
- **/
-static gint
-gs_shell_sources_list_sort_func (GtkListBoxRow *a,
-                                GtkListBoxRow *b,
-                                gpointer user_data)
-{
-       return a < b;
-}
-
-/**
- * gs_shell_sources_add_app:
- **/
-static void
-gs_shell_sources_add_app (GtkListBox *listbox, GsApp *app)
-{
-       GtkWidget *box;
-       GtkWidget *widget;
-
-       box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
-       gtk_widget_set_margin_top (box, 12);
-       gtk_widget_set_margin_start (box, 12);
-       gtk_widget_set_margin_bottom (box, 12);
-       gtk_widget_set_margin_end (box, 12);
-
-       widget = gtk_label_new (gs_app_get_name (app));
-       gtk_widget_set_halign (widget, GTK_ALIGN_START);
-       gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
-
-       gtk_list_box_prepend (listbox, box);
-       gtk_widget_show (widget);
-       gtk_widget_show (box);
-}
-
-/**
- * gs_shell_sources_list_row_activated_cb:
- **/
-static void
-gs_shell_sources_list_row_activated_cb (GtkListBox *list_box,
-                                       GtkListBoxRow *row,
-                                       GsShell *shell)
-{
-       GPtrArray *related;
-       GsApp *app;
-       GsShellPrivate *priv = shell->priv;
-       GtkWidget *widget;
-       guint cnt_apps = 0;
-       guint i;
-
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_sources"));
-       gtk_stack_set_visible_child_name (GTK_STACK (widget), "source-details");
-
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_back"));
-       gtk_widget_show (widget);
-
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "listbox_sources_apps"));
-       gs_container_remove_all (GTK_CONTAINER (widget));
-       app = GS_APP (g_object_get_data (G_OBJECT (gtk_bin_get_child (GTK_BIN (row))), 
-                                        "GsShell::app"));
-       related = gs_app_get_related (app);
-       for (i = 0; i < related->len; i++) {
-               app = g_ptr_array_index (related, i);
-               switch (gs_app_get_kind (app)) {
-               case GS_APP_KIND_NORMAL:
-               case GS_APP_KIND_SYSTEM:
-                       gs_shell_sources_add_app (GTK_LIST_BOX (widget), app);
-                       cnt_apps++;
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       /* save this */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_sources"));
-       g_object_set_data_full (G_OBJECT (widget), "GsShell::app",
-                               g_object_ref (app),
-                               (GDestroyNotify) g_object_unref);
-
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "scrolledwindow_sources_apps"));
-       gtk_widget_set_visible (widget, cnt_apps != 0);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "label_sources2"));
-       gtk_widget_set_visible (widget, cnt_apps != 0);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "grid_sources_noresults"));
-       gtk_widget_set_visible (widget, cnt_apps == 0);
-}
-
-/**
- * gs_shell_sources_back_button_cb:
- **/
-static void
-gs_shell_sources_back_button_cb (GtkWidget *widget, GsShell *shell)
-{
-       GsShellPrivate *priv = shell->priv;
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_back"));
-       gtk_widget_hide (widget);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_sources"));
-       gtk_stack_set_visible_child_name (GTK_STACK (widget), "sources");
-}
-
-
-/**
- * gs_shell_sources_app_removed_cb:
- **/
-static void
-gs_shell_sources_app_removed_cb (GObject *source,
-                                GAsyncResult *res,
-                                gpointer user_data)
-{
-       GError *error = NULL;
-       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
-       GsShell *shell = GS_SHELL (user_data);
-       GsShellPrivate *priv = shell->priv;
-       GtkWidget *widget;
-       gboolean ret;
-
-       ret = gs_plugin_loader_app_action_finish (plugin_loader,
-                                                 res,
-                                                 &error);
-       if (!ret) {
-               g_warning ("failed to remove: %s", error->message);
-               g_error_free (error);
-       } else {
-               gs_shell_show_sources (shell);
-       }
-
-       /* enable button */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_remove"));
-       gtk_widget_set_sensitive (widget, TRUE);
-       gtk_button_set_label (GTK_BUTTON (widget), _("Remove Source"));
-
-       /* allow going back */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_back"));
-       gtk_widget_set_sensitive (widget, TRUE);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "listbox_sources_apps"));
-       gtk_widget_set_sensitive (widget, TRUE);
-}
-
-/**
- * gs_shell_sources_remove_button_cb:
- **/
-static void
-gs_shell_sources_remove_button_cb (GtkWidget *widget, GsShell *shell)
-{
-       GsApp *app;
-       GsShellPrivate *priv = shell->priv;
-
-       /* disable button */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_remove"));
-       gtk_widget_set_sensitive (widget, FALSE);
-       gtk_button_set_label (GTK_BUTTON (widget), _("Removing…"));
-
-       /* disallow going back */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_back"));
-       gtk_widget_set_sensitive (widget, FALSE);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "listbox_sources_apps"));
-       gtk_widget_set_sensitive (widget, FALSE);
-
-       /* remove source */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_sources"));
-       app = GS_APP (g_object_get_data (G_OBJECT (widget), "GsShell::app"));
-       gs_plugin_loader_app_action_async (priv->plugin_loader,
-                                          app,
-                                          GS_PLUGIN_LOADER_ACTION_REMOVE,
-                                          priv->cancellable,
-                                          gs_shell_sources_app_removed_cb,
-                                          shell);
-}
-
-/**
  * gs_shell_setup:
  */
 GtkWindow *
@@ -704,9 +521,6 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
                           GINT_TO_POINTER (GS_SHELL_MODE_UPDATES));
        g_signal_connect (widget, "clicked",
                          G_CALLBACK (gs_shell_overview_button_cb), shell);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_remove"));
-       g_signal_connect (widget, "clicked",
-                         G_CALLBACK (gs_shell_sources_remove_button_cb), shell);
 
        gs_shell_overview_setup (priv->shell_overview,
                                 shell,
@@ -752,40 +566,6 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
        g_signal_connect (widget, "notify::text",
                          G_CALLBACK (text_changed_handler), shell);
 
-       /* set up sources */
-       main_window = GTK_WIDGET (gtk_builder_get_object (priv->builder, "window_sources"));
-       g_signal_connect (main_window, "delete-event",
-                         G_CALLBACK (gtk_widget_hide_on_delete), NULL);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_sources"));
-       g_object_ref (widget);
-       gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (widget)), widget);
-       gtk_window_set_titlebar (GTK_WINDOW (main_window), widget);
-       g_object_unref (widget);
-
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "listbox_sources"));
-       gtk_list_box_set_header_func (GTK_LIST_BOX (widget),
-                                     gs_shell_sources_list_header_func,
-                                     shell,
-                                     NULL);
-       gtk_list_box_set_sort_func (GTK_LIST_BOX (widget),
-                                   gs_shell_sources_list_sort_func,
-                                   shell, NULL);
-       g_signal_connect (widget, "row-activated",
-                         G_CALLBACK (gs_shell_sources_list_row_activated_cb), shell);
-
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "listbox_sources_apps"));
-       gtk_list_box_set_header_func (GTK_LIST_BOX (widget),
-                                     gs_shell_sources_list_header_func,
-                                     shell,
-                                     NULL);
-       gtk_list_box_set_sort_func (GTK_LIST_BOX (widget),
-                                   gs_shell_sources_list_sort_func,
-                                   shell, NULL);
-
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_back"));
-       g_signal_connect (widget, "clicked",
-                         G_CALLBACK (gs_shell_sources_back_button_cb), shell);
-
        /* load content */
        g_signal_connect (priv->shell_overview, "refreshed",
                          G_CALLBACK (initial_overview_load_done), shell);
@@ -822,158 +602,17 @@ gs_shell_get_mode (GsShell *shell)
        return priv->mode;
 }
 
-/**
- * gs_shell_sources_add_source:
- **/
-static void
-gs_shell_sources_add_source (GtkListBox *listbox, GsApp *app)
-{
-       GsApp *app_tmp;
-       GtkWidget *widget;
-       GtkWidget *box;
-       GtkStyleContext *context;
-       GPtrArray *related;
-       gchar *text;
-       guint cnt_addon = 0;
-       guint cnt_apps = 0;
-       guint i;
-
-       box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-       gtk_widget_set_margin_top (box, 12);
-       gtk_widget_set_margin_start (box, 12);
-       gtk_widget_set_margin_bottom (box, 12);
-       gtk_widget_set_margin_end (box, 12);
-
-       widget = gtk_label_new (gs_app_get_name (app));
-       gtk_widget_set_halign (widget, GTK_ALIGN_START);
-       gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
-       related = gs_app_get_related (app);
-
-       /* split up the types */
-       for (i = 0; i < related->len; i++) {
-               app_tmp = g_ptr_array_index (related, i);
-               switch (gs_app_get_id_kind (app_tmp)) {
-               case GS_APP_ID_KIND_WEBAPP:
-               case GS_APP_ID_KIND_DESKTOP:
-                       cnt_apps++;
-                       break;
-               case GS_APP_ID_KIND_FONT:
-               case GS_APP_ID_KIND_CODEC:
-               case GS_APP_ID_KIND_INPUT_METHOD:
-                       cnt_addon++;
-                       break;
-               default:
-                       break;
-               }
-       }
-       if (cnt_apps == 0 && cnt_addon == 0) {
-               /* TRANSLATORS: this source has no apps installed from it */
-               text = g_strdup (_("No software installed"));
-       } else if (cnt_addon == 0) {
-               /* TRANSLATORS: this source has some apps installed from it */
-               text = g_strdup_printf (ngettext ("%i application installed",
-                                                 "%i applications installed",
-                                                 cnt_apps), cnt_apps);
-       } else if (cnt_apps == 0) {
-               /* TRANSLATORS: this source has some apps installed from it */
-               text = g_strdup_printf (ngettext ("%i add-on installed",
-                                                 "%i add-ons installed",
-                                                 cnt_addon), cnt_addon);
-       } else {
-               /* TRANSLATORS: this source has some apps and addons installed from it */
-               text = g_strdup_printf (ngettext ("%i application and %i add-ons installed",
-                                                 "%i applications and %i add-ons installed",
-                                                 cnt_apps),
-                                       cnt_apps, cnt_addon);
-       }
-       widget = gtk_label_new (text);
-       g_free (text);
-       gtk_widget_set_halign (widget, GTK_ALIGN_START);
-       gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
-
-       context = gtk_widget_get_style_context (widget);
-       gtk_style_context_add_class (context, "dim-label");
-       g_object_set_data_full (G_OBJECT (box), "GsShell::app",
-                               g_object_ref (app),
-                               (GDestroyNotify) g_object_unref);
-
-       gtk_list_box_prepend (listbox, box);
-       gtk_widget_show_all (box);
-}
-
-/**
- * gs_shell_sources_get_sources_cb:
- **/
-static void
-gs_shell_sources_get_sources_cb (GsPluginLoader *plugin_loader,
-                                GAsyncResult *res,
-                                GsShell *shell)
-{
-       GError *error = NULL;
-       GList *l;
-       GList *list;
-       GsApp *app;
-       GtkWidget *widget;
-       GsShellPrivate *priv = shell->priv;
-
-       /* show results */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "spinner_sources"));
-       gtk_spinner_stop (GTK_SPINNER (widget));
-
-       /* get the results */
-       list = gs_plugin_loader_get_sources_finish (plugin_loader, res, &error);
-       if (list == NULL) {
-               if (g_error_matches (error,
-                                    GS_PLUGIN_LOADER_ERROR,
-                                    GS_PLUGIN_LOADER_ERROR_NO_RESULTS)) {
-                       g_debug ("no sources to show");
-               } else {
-                       g_warning ("failed to get sources: %s", error->message);
-               }
-               g_error_free (error);
-               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_sources"));
-               gtk_stack_set_visible_child_name (GTK_STACK (widget), "sources-empty");
-               goto out;
-       }
-
-       /* add each */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_sources"));
-       gtk_stack_set_visible_child_name (GTK_STACK (widget), "sources");
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
-               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "listbox_sources"));
-               gs_shell_sources_add_source (GTK_LIST_BOX (widget), app);
-       }
-out:
-       if (list != NULL)
-               gs_plugin_list_free (list);
-}
-
 void
 gs_shell_show_sources (GsShell *shell)
 {
        GsShellPrivate *priv = shell->priv;
-       GtkWidget *widget;
+       GtkWidget *dialog;
+       GtkWidget *toplevel;
 
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_sources"));
-       gtk_stack_set_visible_child_name (GTK_STACK (widget), "sources-waiting");
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "spinner_sources"));
-       gtk_spinner_start (GTK_SPINNER (widget));
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_sources_back"));
-       gtk_widget_hide (widget);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "listbox_sources"));
-       gs_container_remove_all (GTK_CONTAINER (widget));
-
-       /* get the list of non-core software sources */
-       gs_plugin_loader_get_sources_async (priv->plugin_loader,
-                                           GS_PLUGIN_REFINE_FLAGS_DEFAULT |
-                                           GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED,
-                                           priv->cancellable,
-                                           (GAsyncReadyCallback) gs_shell_sources_get_sources_cb,
-                                           shell);
-
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "window_sources"));
-       gtk_window_present (GTK_WINDOW (widget));
+       toplevel = GTK_WIDGET (gtk_builder_get_object (priv->builder, "window_software"));
+       dialog = gs_sources_dialog_new (GTK_WINDOW (toplevel), priv->plugin_loader);
+
+       gtk_window_present (GTK_WINDOW (dialog));
 }
 
 void
diff --git a/src/gs-sources-dialog.c b/src/gs-sources-dialog.c
new file mode 100644
index 0000000..b97b81a
--- /dev/null
+++ b/src/gs-sources-dialog.c
@@ -0,0 +1,428 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-sources-dialog.h"
+#include "gs-utils.h"
+
+struct _GsSourcesDialogPrivate
+{
+       GCancellable    *cancellable;
+       GsPluginLoader  *plugin_loader;
+       GtkWidget       *button_back;
+       GtkWidget       *button_remove;
+       GtkWidget       *grid_noresults;
+       GtkWidget       *header;
+       GtkWidget       *label2;
+       GtkWidget       *listbox;
+       GtkWidget       *listbox_apps;
+       GtkWidget       *scrolledwindow_apps;
+       GtkWidget       *spinner;
+       GtkWidget       *stack;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsSourcesDialog, gs_sources_dialog, GTK_TYPE_DIALOG)
+
+static void
+add_source (GtkListBox *listbox, GsApp *app)
+{
+       GsApp *app_tmp;
+       GtkWidget *widget;
+       GtkWidget *box;
+       GtkStyleContext *context;
+       GPtrArray *related;
+       gchar *text;
+       guint cnt_addon = 0;
+       guint cnt_apps = 0;
+       guint i;
+
+       box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       gtk_widget_set_margin_top (box, 12);
+       gtk_widget_set_margin_start (box, 12);
+       gtk_widget_set_margin_bottom (box, 12);
+       gtk_widget_set_margin_end (box, 12);
+
+       widget = gtk_label_new (gs_app_get_name (app));
+       gtk_widget_set_halign (widget, GTK_ALIGN_START);
+       gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+       related = gs_app_get_related (app);
+
+       /* split up the types */
+       for (i = 0; i < related->len; i++) {
+               app_tmp = g_ptr_array_index (related, i);
+               switch (gs_app_get_id_kind (app_tmp)) {
+               case GS_APP_ID_KIND_WEBAPP:
+               case GS_APP_ID_KIND_DESKTOP:
+                       cnt_apps++;
+                       break;
+               case GS_APP_ID_KIND_FONT:
+               case GS_APP_ID_KIND_CODEC:
+               case GS_APP_ID_KIND_INPUT_METHOD:
+                       cnt_addon++;
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (cnt_apps == 0 && cnt_addon == 0) {
+               /* TRANSLATORS: this source has no apps installed from it */
+               text = g_strdup (_("No software installed"));
+       } else if (cnt_addon == 0) {
+               /* TRANSLATORS: this source has some apps installed from it */
+               text = g_strdup_printf (ngettext ("%i application installed",
+                                                 "%i applications installed",
+                                                 cnt_apps), cnt_apps);
+       } else if (cnt_apps == 0) {
+               /* TRANSLATORS: this source has some apps installed from it */
+               text = g_strdup_printf (ngettext ("%i add-on installed",
+                                                 "%i add-ons installed",
+                                                 cnt_addon), cnt_addon);
+       } else {
+               /* TRANSLATORS: this source has some apps and addons installed from it */
+               text = g_strdup_printf (ngettext ("%i application and %i add-ons installed",
+                                                 "%i applications and %i add-ons installed",
+                                                 cnt_apps),
+                                       cnt_apps, cnt_addon);
+       }
+       widget = gtk_label_new (text);
+       g_free (text);
+       gtk_widget_set_halign (widget, GTK_ALIGN_START);
+       gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+
+       context = gtk_widget_get_style_context (widget);
+       gtk_style_context_add_class (context, "dim-label");
+       g_object_set_data_full (G_OBJECT (box), "GsShell::app",
+                               g_object_ref (app),
+                               (GDestroyNotify) g_object_unref);
+
+       gtk_list_box_prepend (listbox, box);
+       gtk_widget_show_all (box);
+}
+
+static void
+get_sources_cb (GsPluginLoader *plugin_loader,
+                GAsyncResult *res,
+                GsSourcesDialog *dialog)
+{
+       GError *error = NULL;
+       GList *l;
+       GList *list;
+       GsApp *app;
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+
+       /* show results */
+       gtk_spinner_stop (GTK_SPINNER (priv->spinner));
+
+       /* get the results */
+       list = gs_plugin_loader_get_sources_finish (plugin_loader, res, &error);
+       if (list == NULL) {
+               if (g_error_matches (error,
+                                    GS_PLUGIN_LOADER_ERROR,
+                                    GS_PLUGIN_LOADER_ERROR_NO_RESULTS)) {
+                       g_debug ("no sources to show");
+               } else {
+                       g_warning ("failed to get sources: %s", error->message);
+               }
+               g_error_free (error);
+               gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "empty");
+               goto out;
+       }
+
+       /* add each */
+       gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "sources");
+       for (l = list; l != NULL; l = l->next) {
+               app = GS_APP (l->data);
+               add_source (GTK_LIST_BOX (priv->listbox), app);
+       }
+out:
+       if (list != NULL)
+               gs_plugin_list_free (list);
+}
+
+static void
+reload_sources (GsSourcesDialog *dialog)
+{
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+
+       gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "waiting");
+       gtk_spinner_start (GTK_SPINNER (priv->spinner));
+       gtk_widget_hide (priv->button_back);
+       gs_container_remove_all (GTK_CONTAINER (priv->listbox));
+
+       /* get the list of non-core software sources */
+       gs_plugin_loader_get_sources_async (priv->plugin_loader,
+                                           GS_PLUGIN_REFINE_FLAGS_DEFAULT |
+                                           GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED,
+                                           priv->cancellable,
+                                           (GAsyncReadyCallback) get_sources_cb,
+                                           dialog);
+}
+
+static void
+list_header_func (GtkListBoxRow *row,
+                  GtkListBoxRow *before,
+                  gpointer user_data)
+{
+       GtkWidget *header = NULL;
+       if (before != NULL)
+               header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+       gtk_list_box_row_set_header (row, header);
+}
+
+static gint
+list_sort_func (GtkListBoxRow *a,
+                GtkListBoxRow *b,
+                gpointer user_data)
+{
+       return a < b;
+}
+
+static void
+add_app (GtkListBox *listbox, GsApp *app)
+{
+       GtkWidget *box;
+       GtkWidget *widget;
+
+       box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+       gtk_widget_set_margin_top (box, 12);
+       gtk_widget_set_margin_start (box, 12);
+       gtk_widget_set_margin_bottom (box, 12);
+       gtk_widget_set_margin_end (box, 12);
+
+       widget = gtk_label_new (gs_app_get_name (app));
+       gtk_widget_set_halign (widget, GTK_ALIGN_START);
+       gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+
+       gtk_list_box_prepend (listbox, box);
+       gtk_widget_show (widget);
+       gtk_widget_show (box);
+}
+
+static void
+list_row_activated_cb (GtkListBox *list_box,
+                       GtkListBoxRow *row,
+                       GsSourcesDialog *dialog)
+{
+       GPtrArray *related;
+       GsApp *app;
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+       guint cnt_apps = 0;
+       guint i;
+
+       gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "details");
+
+       gtk_widget_show (priv->button_back);
+
+       gs_container_remove_all (GTK_CONTAINER (priv->listbox_apps));
+       app = GS_APP (g_object_get_data (G_OBJECT (gtk_bin_get_child (GTK_BIN (row))), 
+                                        "GsShell::app"));
+       related = gs_app_get_related (app);
+       for (i = 0; i < related->len; i++) {
+               app = g_ptr_array_index (related, i);
+               switch (gs_app_get_kind (app)) {
+               case GS_APP_KIND_NORMAL:
+               case GS_APP_KIND_SYSTEM:
+                       add_app (GTK_LIST_BOX (priv->listbox_apps), app);
+                       cnt_apps++;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /* save this */
+       g_object_set_data_full (G_OBJECT (priv->stack), "GsShell::app",
+                               g_object_ref (app),
+                               (GDestroyNotify) g_object_unref);
+
+       gtk_widget_set_visible (priv->scrolledwindow_apps, cnt_apps != 0);
+       gtk_widget_set_visible (priv->label2, cnt_apps != 0);
+       gtk_widget_set_visible (priv->grid_noresults, cnt_apps == 0);
+}
+
+static void
+back_button_cb (GtkWidget *widget, GsSourcesDialog *dialog)
+{
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+       gtk_widget_hide (priv->button_back);
+       gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "sources");
+}
+
+static void
+app_removed_cb (GObject *source,
+                GAsyncResult *res,
+                gpointer user_data)
+{
+       GError *error = NULL;
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+       GsSourcesDialog *dialog = GS_SOURCES_DIALOG (user_data);
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+       gboolean ret;
+
+       ret = gs_plugin_loader_app_action_finish (plugin_loader,
+                                                 res,
+                                                 &error);
+       if (!ret) {
+               g_warning ("failed to remove: %s", error->message);
+               g_error_free (error);
+       } else {
+               reload_sources (dialog);
+       }
+
+       /* enable button */
+       gtk_widget_set_sensitive (priv->button_remove, TRUE);
+       gtk_button_set_label (GTK_BUTTON (priv->button_remove), _("Remove Source"));
+
+       /* allow going back */
+       gtk_widget_set_sensitive (priv->button_back, TRUE);
+       gtk_widget_set_sensitive (priv->listbox_apps, TRUE);
+}
+
+static void
+remove_button_cb (GtkWidget *widget, GsSourcesDialog *dialog)
+{
+       GsApp *app;
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+
+       /* disable button */
+       gtk_widget_set_sensitive (priv->button_remove, FALSE);
+       gtk_button_set_label (GTK_BUTTON (priv->button_remove), _("Removing…"));
+
+       /* disallow going back */
+       gtk_widget_set_sensitive (priv->button_back, FALSE);
+       gtk_widget_set_sensitive (priv->listbox_apps, FALSE);
+
+       /* remove source */
+       app = GS_APP (g_object_get_data (G_OBJECT (priv->stack), "GsShell::app"));
+       gs_plugin_loader_app_action_async (priv->plugin_loader,
+                                          app,
+                                          GS_PLUGIN_LOADER_ACTION_REMOVE,
+                                          priv->cancellable,
+                                          app_removed_cb,
+                                          dialog);
+}
+
+static void
+set_plugin_loader (GsSourcesDialog *dialog, GsPluginLoader *plugin_loader)
+{
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+
+       priv->plugin_loader = g_object_ref (plugin_loader);
+}
+
+static void
+gs_sources_dialog_finalize (GObject *object)
+{
+       GsSourcesDialog *dialog = GS_SOURCES_DIALOG (object);
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+
+       g_clear_object (&priv->plugin_loader);
+
+       if (priv->cancellable != NULL) {
+               g_cancellable_cancel (priv->cancellable);
+               g_clear_object (&priv->cancellable);
+       }
+
+       G_OBJECT_CLASS (gs_sources_dialog_parent_class)->finalize (object);
+}
+
+static void
+gs_sources_dialog_init (GsSourcesDialog *dialog)
+{
+       GsSourcesDialogPrivate *priv = gs_sources_dialog_get_instance_private (dialog);
+
+       gtk_widget_init_template (GTK_WIDGET (dialog));
+
+       priv->cancellable = g_cancellable_new ();
+
+       g_object_ref (priv->header);
+       gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->header)), priv->header);
+       gtk_window_set_titlebar (GTK_WINDOW (dialog), priv->header);
+       g_object_unref (priv->header);
+
+       gtk_list_box_set_header_func (GTK_LIST_BOX (priv->listbox),
+                                     list_header_func,
+                                     dialog,
+                                     NULL);
+       gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->listbox),
+                                   list_sort_func,
+                                   dialog, NULL);
+       g_signal_connect (priv->listbox, "row-activated",
+                         G_CALLBACK (list_row_activated_cb), dialog);
+
+       gtk_list_box_set_header_func (GTK_LIST_BOX (priv->listbox_apps),
+                                     list_header_func,
+                                     dialog,
+                                     NULL);
+       gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->listbox_apps),
+                                   list_sort_func,
+                                   dialog, NULL);
+
+       g_signal_connect (priv->button_back, "clicked",
+                         G_CALLBACK (back_button_cb), dialog);
+       g_signal_connect (priv->button_remove, "clicked",
+                         G_CALLBACK (remove_button_cb), dialog);
+}
+
+static void
+gs_sources_dialog_class_init (GsSourcesDialogClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->finalize = gs_sources_dialog_finalize;
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/software/gs-sources-dialog.ui");
+
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, button_back);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, button_remove);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, grid_noresults);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, header);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, label2);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, listbox);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, listbox_apps);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, scrolledwindow_apps);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, spinner);
+       gtk_widget_class_bind_template_child_private (widget_class, GsSourcesDialog, stack);
+}
+
+GtkWidget *
+gs_sources_dialog_new (GtkWindow *parent, GsPluginLoader *plugin_loader)
+{
+       GsSourcesDialog *dialog;
+
+       dialog = g_object_new (GS_TYPE_SOURCES_DIALOG,
+                              "use-header-bar", TRUE,
+                              "transient-for", parent,
+                              "modal", TRUE,
+                              NULL);
+       set_plugin_loader (dialog, plugin_loader);
+       reload_sources (dialog);
+
+       return GTK_WIDGET (dialog);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-sources-dialog.h b/src/gs-sources-dialog.h
new file mode 100644
index 0000000..17e5e98
--- /dev/null
+++ b/src/gs-sources-dialog.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_SOURCES_DIALOG_H
+#define GS_SOURCES_DIALOG_H
+
+#include <gtk/gtk.h>
+
+#include "gs-app.h"
+#include "gs-plugin-loader.h"
+
+#define GS_TYPE_SOURCES_DIALOG                 (gs_sources_dialog_get_type())
+#define GS_SOURCES_DIALOG(obj)                 (G_TYPE_CHECK_INSTANCE_CAST((obj), GS_TYPE_SOURCES_DIALOG, 
GsSourcesDialog))
+#define GS_SOURCES_DIALOG_CLASS(cls)           (G_TYPE_CHECK_CLASS_CAST((cls), GS_TYPE_SOURCES_DIALOG, 
GsSourcesDialogClass))
+#define GS_IS_SOURCES_DIALOG(obj)              (G_TYPE_CHECK_INSTANCE_TYPE((obj), GS_TYPE_SOURCES_DIALOG))
+#define GS_IS_SOURCES_DIALOG_CLASS(cls)                (G_TYPE_CHECK_CLASS_TYPE((cls), 
GS_TYPE_SOURCES_DIALOG))
+#define GS_SOURCES_DIALOG_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS((obj), GS_TYPE_SOURCES_DIALOG, 
GsSourcesDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GsSourcesDialog                GsSourcesDialog;
+typedef struct _GsSourcesDialogClass   GsSourcesDialogClass;
+typedef struct _GsSourcesDialogPrivate GsSourcesDialogPrivate;
+
+struct _GsSourcesDialog
+{
+       GtkDialog        parent;
+};
+
+struct _GsSourcesDialogClass
+{
+       GtkDialogClass   parent_class;
+};
+
+GType           gs_sources_dialog_get_type     (void);
+GtkWidget      *gs_sources_dialog_new          (GtkWindow      *parent,
+                                                GsPluginLoader *plugin_loader);
+
+#endif /* GS_SOURCES_DIALOG_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-sources-dialog.ui b/src/gs-sources-dialog.ui
new file mode 100644
index 0000000..6563cf0
--- /dev/null
+++ b/src/gs-sources-dialog.ui
@@ -0,0 +1,485 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+  <template class="GsSourcesDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Software Sources</property>
+    <property name="modal">True</property>
+    <property name="default_width">460</property>
+    <property name="default_height">500</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="skip_taskbar_hint">True</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHeaderBar" id="header">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="show_close_button">True</property>
+
+            <child>
+              <object class="GtkButton" id="button_back">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <child internal-child="accessible">
+                  <object class="AtkObject" id="button_back_accessible">
+                    <property name="accessible-name" translatable="yes">Go back</property>
+                  </object>
+                </child>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage" id="image_back">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">go-previous-symbolic</property>
+                    <property name="icon_size">1</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="title">
+              <object class="GtkLabel" id="label_header">
+                <property name="can_focus">False</property>
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Software Sources</property>
+                <property name="selectable">False</property>
+                <style>
+                  <class name="title"/>
+                </style>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkSpinner" id="spinner">
+                <property name="visible">True</property>
+                <property name="width_request">128</property>
+                <property name="height_request">128</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="hexpand">True</property>
+                <property name="vexpand">True</property>
+              </object>
+              <packing>
+                <property name="name">waiting</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box_empty">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel" id="label_empty">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="justify">center</property>
+                    <property name="label" translatable="yes">No sources found.</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">empty</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_left">16</property>
+                <property name="margin_right">16</property>
+                <property name="margin_top">16</property>
+                <property name="margin_bottom">32</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">4</property>
+                <child>
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Software sources give you access to additional 
software.</property>
+                    <property name="wrap">True</property>
+                    <property name="max_width_chars">30</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Removing a source will also remove any 
software you have installed from it.</property>
+                    <property name="wrap">True</property>
+                    <property name="max_width_chars">30</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">none</property>
+                    <property name="margin_top">9</property>
+                    <child>
+                      <object class="GtkFrame" id="frame">
+                        <property name="visible">True</property>
+                        <property name="shadow_type">in</property>
+                        <property name="halign">fill</property>
+                        <property name="valign">start</property>
+                        <child>
+                          <object class="GtkListBox" id="listbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="selection_mode">none</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">sources</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_left">16</property>
+                <property name="margin_right">16</property>
+                <property name="margin_top">16</property>
+                <property name="margin_bottom">16</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">4</property>
+                <child>
+                  <object class="GtkGrid" id="grid_noresults">
+                    <property name="visible">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <property name="row-spacing">12</property>
+                    <property name="column-spacing">12</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                    <child>
+                      <object class="GtkImage" id="image_noresults">
+                        <property name="visible">True</property>
+                        <property name="icon_name">gnome-software-symbolic</property>
+                        <property name="pixel-size">64</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left-attach">0</property>
+                        <property name="top-attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_noresults">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">No software installed from this 
source</property>
+                        <property name="halign">center</property>
+                        <property name="valign">center</property>
+                        <property name="justify">center</property>
+                        <property name="wrap">True</property>
+                        <property name="max_width_chars">25</property>
+                        <attributes>
+                          <attribute name="scale" value="1.2"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="left-attach">0</property>
+                        <property name="top-attach">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0.5</property>
+                    <property name="label" translatable="yes">Installed from this Source</property>
+                    <property name="wrap">True</property>
+                    <property name="max_width_chars">30</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow_apps">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkFrame" id="frame_apps">
+                        <property name="visible">True</property>
+                        <property name="shadow_type">in</property>
+                        <property name="halign">fill</property>
+                        <property name="valign">start</property>
+                        <child>
+                          <object class="GtkListBox" id="listbox_apps">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="selection_mode">none</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label_details">
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Source Details</property>
+                    <property name="wrap">True</property>
+                    <property name="max_width_chars">30</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkGrid" id="grid_details">
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="valign">start</property>
+                    <property name="row_spacing">3</property>
+                    <property name="column_spacing">24</property>
+                    <property name="row_homogeneous">True</property>
+                    <child>
+                      <object class="GtkLabel" id="label_header_version">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Version</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_version">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label">0.12.3</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_header_lastchecked">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Last Checked</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">2</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_header_added">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Added</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_header_website">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Website</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">3</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_added">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label">May 12, 2012</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_website">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label">superrepo.com</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">3</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_lastchecked">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">January 30, 2014</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">2</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="button_remove">
+                    <property name="label" translatable="yes">Remove Source</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="margin_top">12</property>
+                    <style>
+                      <class name="destructive-action"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">4</property>
+                    <property name="pack_type">end</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">details</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>


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