[gnome-contacts] Implement AvatarDialog as a GtkTemplate.



commit a59a7bcc371ec9ebaf74cd8ba488298a209a785e
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Mon Jun 26 11:35:20 2017 +0200

    Implement AvatarDialog as a GtkTemplate.
    
    * The thumbnails are now loaded using a Gtk.FlowBox, which saves a lot
    of code.
    * Cheese's `coldplug()` is now done asynchronously, since it otherwise
    blocks the UI for several seconds.
    * Minor formatting changes.
    * Add some comments to make things clearer.

 data/Makefile.am                  |    1 +
 data/contacts.gresource.xml       |    1 +
 data/ui/contacts-avatar-dialog.ui |  275 +++++++++++++++++++
 data/ui/style.css                 |    4 +
 po/POTFILES.in                    |    1 +
 src/contacts-avatar-dialog.vala   |  547 +++++++++++++++----------------------
 6 files changed, 509 insertions(+), 320 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 0f52d3d..da230ba 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -36,6 +36,7 @@ EXTRA_DIST = \
        contacts.gresource.xml \
        ui/app-menu.ui \
        ui/contacts-address-map.ui \
+       ui/contacts-avatar-dialog.ui \
        ui/contacts-in-app-notification.ui \
        ui/contacts-list-pane.ui \
        ui/contacts-window.ui \
diff --git a/data/contacts.gresource.xml b/data/contacts.gresource.xml
index 9f29f00..a40f05a 100644
--- a/data/contacts.gresource.xml
+++ b/data/contacts.gresource.xml
@@ -4,6 +4,7 @@
     <file compressed="true">ui/style.css</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/app-menu.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-address-map.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">ui/contacts-avatar-dialog.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-in-app-notification.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-list-pane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/contacts-window.ui</file>
diff --git a/data/ui/contacts-avatar-dialog.ui b/data/ui/contacts-avatar-dialog.ui
new file mode 100644
index 0000000..5684f98
--- /dev/null
+++ b/data/ui/contacts-avatar-dialog.ui
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <template class="ContactsAvatarDialog" parent="GtkDialog">
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">Select Picture</property>
+    <property name="modal">True</property>
+    <style>
+      <class name="contacts-avatar-dialog"/>
+    </style>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkGrid" id="grid">
+            <property name="visible">True</property>
+            <property name="border_width">8</property>
+            <property name="column_spacing">16</property>
+            <property name="row_spacing">11</property>
+            <child>
+            </child>
+              <!-- ContactFrame -->
+              <placeholder/>
+            <child>
+              <object class="GtkLabel" id="contact_name_label">
+                <property name="visible">True</property>
+                <property name="halign">start</property>
+                <property name="valign">start</property>
+                <property name="hexpand">True</property>
+                <property name="margin_top">4</property>
+                <property name="ellipsize">end</property>
+                <property name="label" translatable="yes">New Contact</property>
+                <style>
+                  <class name="contact-display-name"/>
+                </style>
+              </object>
+              <packing>
+                <property name="top_attach">0</property>
+                <property name="left_attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame">
+                <property name="visible">True</property>
+                <style>
+                  <class name="contacts-avatar-frame"/>
+                </style>
+                <child>
+                  <object class="GtkStack" id="views_stack">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkGrid" id="thumbnail_page">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkScrolledWindow">
+                            <property name="visible">True</property>
+                            <property name="hscrollbar_policy">never</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="hexpand">True</property>
+                            <property name="vexpand">True</property>
+                            <property name="height_request">300</property>
+                            <child>
+                              <object class="GtkFlowBox" id="thumbnail_grid">
+                                <property name="visible">True</property>
+                                <property name="min_children_per_line">5</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkActionBar">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkBox" id="webcam_button_box">
+                                <property name="orientation">horizontal</property>
+                                <style>
+                                  <class name="linked"/>
+                                </style>
+                                <child>
+                                  <object class="GtkButton">
+                                    <property name="visible">True</property>
+                                    <signal name="clicked" handler="select_avatar_file_cb" swapped="no"/>
+                                    <child>
+                                      <object class="GtkImage">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="pixel_size">16</property>
+                                        <property name="icon_name">list-add-symbolic</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkButton" id="webcam_button">
+                                    <property name="visible">True</property>
+                                    <property name="sensitive">False</property>
+                                    <signal name="clicked" handler="on_webcam_button_clicked" swapped="no"/>
+                                    <child>
+                                      <object class="GtkImage">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="pixel_size">16</property>
+                                        <property name="icon_name">camera-photo-symbolic</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkButton">
+                                <property name="visible" bind-source="webcam_button_box" 
bind-property="visible" bind-flags="invert-boolean|sync-create" />
+                                <signal name="clicked" handler="select_avatar_file_cb" swapped="no"/>
+                                <child>
+                                  <object class="GtkImage">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="pixel_size">16</property>
+                                    <property name="icon_name">list-add-symbolic</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="name">thumbnail-page</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkGrid" id="crop_page">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkActionBar">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="orientation">horizontal</property>
+                                <style>
+                                  <class name="linked"/>
+                                </style>
+                                <child>
+                                  <object class="GtkButton">
+                                    <property name="visible">True</property>
+                                    <signal name="clicked" handler="on_crop_page_select_button_clicked" 
swapped="no"/>
+                                    <child>
+                                      <object class="GtkImage">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="pixel_size">16</property>
+                                        <property name="icon_name">object-select-symbolic</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkButton">
+                                    <property name="visible">True</property>
+                                    <signal name="clicked" handler="on_crop_page_cancel_button_clicked" 
swapped="no"/>
+                                    <child>
+                                      <object class="GtkImage">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="pixel_size">16</property>
+                                        <property name="icon_name">edit-undo-symbolic</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="left_attach">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="name">crop-page</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkGrid" id="photobooth_page">
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkActionBar">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="orientation">horizontal</property>
+                                <style>
+                                  <class name="linked"/>
+                                </style>
+                                <child>
+                                  <object class="GtkButton">
+                                    <property name="visible">True</property>
+                                    <signal name="clicked" 
handler="on_photobooth_page_select_button_clicked" swapped="no"/>
+                                    <child>
+                                      <object class="GtkImage">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="pixel_size">16</property>
+                                        <property name="icon_name">object-select-symbolic</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkButton">
+                                    <property name="visible">True</property>
+                                    <signal name="clicked" 
handler="on_photobooth_page_cancel_button_clicked" swapped="no"/>
+                                    <child>
+                                      <object class="GtkImage">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="pixel_size">16</property>
+                                        <property name="icon_name">edit-undo-symbolic</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="left_attach">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="name">photobooth-page</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="left_attach">0</property>
+                <property name="width">2</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+
+    <child type="action">
+      <object class="GtkButton" id="select_button">
+        <property name="visible">True</property>
+        <property name="can-default">True</property>
+        <property name="sensitive">False</property>
+        <property name="label" translatable="yes">Select</property>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="cancel_button">
+        <property name="visible">True</property>
+        <property name="label" translatable="yes">Cancel</property>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="cancel">cancel_button</action-widget>
+      <action-widget response="ok" default="true">select_button</action-widget>
+    </action-widgets>
+  </template>
+</interface>
diff --git a/data/ui/style.css b/data/ui/style.css
index 7ecf854..bb29b75 100644
--- a/data/ui/style.css
+++ b/data/ui/style.css
@@ -117,3 +117,7 @@ ContactsWindow .primary-toolbar.toolbar {
   color: #bebebe;
   text-shadow: 1px 1px alpha(@theme_base_color, 0.6);
 }
+
+.contacts-avatar-dialog .contact-display-name {
+  font-size: 20px;
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 664d3c8..ab747aa 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -3,6 +3,7 @@
 data/org.gnome.Contacts.appdata.xml.in
 data/org.gnome.Contacts.desktop.in
 data/ui/app-menu.ui
+data/ui/contacts-avatar-dialog.ui
 data/ui/contacts-list-pane.ui
 data/ui/contacts-window.ui
 src/contacts-accounts-list.vala
diff --git a/src/contacts-avatar-dialog.vala b/src/contacts-avatar-dialog.vala
index b9674e0..bef1156 100644
--- a/src/contacts-avatar-dialog.vala
+++ b/src/contacts-avatar-dialog.vala
@@ -19,16 +19,42 @@
 using Gtk;
 using Folks;
 
+/**
+ * The AvatarDialog can be used to choose the avatar for a contact.
+ * This can be done by either choosing a stock thumbnail, an image file
+ * provided by the user, or -if cheese is enabled- by using a webcam.
+ *
+ * After a user has initially chosen an avatar, we provide a cropping tool.
+ */
+[GtkTemplate (ui = "/org/gnome/contacts/ui/contacts-avatar-dialog.ui")]
 public class Contacts.AvatarDialog : Dialog {
-  private Gnome.DesktopThumbnailFactory thumbnail_factory;
-  const int main_size = 128;
-  const int icons_size = 64;
-  const int n_columns = 6;
+  const int MAIN_SIZE = 128;
+  const int ICONS_SIZE = 64;
+
   private Contact contact;
+
+  // This will provide the default thumbnails
+  private Gnome.DesktopThumbnailFactory thumbnail_factory;
+
+  [GtkChild]
+  private Grid grid;
+  [GtkChild]
+  private Label contact_name_label;
+  [GtkChild]
   private Stack views_stack;
+  [GtkChild]
+  private FlowBox thumbnail_grid;
+  [GtkChild]
+  private Grid crop_page;
   private Um.CropArea crop_area;
-  private Grid view_grid;
-  private ContactFrame main_frame;
+  [GtkChild]
+  private Grid photobooth_page;
+  [GtkChild]
+  private Button webcam_button;
+  [GtkChild]
+  private Box webcam_button_box;
+
+  private ContactFrame current_avatar;
 
 #if HAVE_CHEESE
   private Cheese.Flash flash;
@@ -39,9 +65,86 @@ public class Contacts.AvatarDialog : Dialog {
 
   private Gdk.Pixbuf? new_pixbuf;
 
+  /**
+   * Fired after the user has definitely chosen a new avatar.
+   */
   public signal void set_avatar (GLib.Icon avatar_icon);
 
-  Gdk.Pixbuf scale_pixbuf_for_avatar_use (Gdk.Pixbuf pixbuf) {
+  public AvatarDialog (Contact? contact) {
+    Object (
+      transient_for: App.app.window,
+      use_header_bar: 1
+    );
+
+    this.thumbnail_factory = new Gnome.DesktopThumbnailFactory (Gnome.ThumbnailSize.NORMAL);
+    this.contact = contact;
+
+    // Load the current avatar
+    this.current_avatar = new ContactFrame (MAIN_SIZE);
+    if (contact != null) {
+      contact.keep_widget_uptodate (this.current_avatar, (w) => {
+          (w as ContactFrame).set_image (contact.individual, contact);
+        });
+    } else {
+      this.current_avatar.set_image (null, null);
+    }
+    this.current_avatar.set_hexpand (false);
+    this.current_avatar.show ();
+    this.grid.attach (this.current_avatar, 0, 0);
+
+    if (contact != null)
+      this.contact_name_label.label = contact.display_name;
+
+#if HAVE_CHEESE
+    this.webcam_button_box.show ();
+
+    // Look for camera devices.
+    this.camera_monitor = new Cheese.CameraDeviceMonitor ();
+    this.camera_monitor.added.connect ( () => {
+        this.num_cameras++;
+        this.webcam_button.sensitive = (this.num_cameras > 0);
+      });
+    this.camera_monitor.removed.connect ( () => {
+        this.num_cameras--;
+        this.webcam_button.sensitive = (this.num_cameras > 0);
+      });
+    // Do this in idle, or it blocks the whole UI
+    Idle.add ( () => {
+        this.camera_monitor.coldplug ();
+        return false;
+      });
+#else
+    // Don't show the camera button
+    this.webcam_button_box.hide ();
+#endif
+
+#if HAVE_CHEESE
+    // Photobooth page
+    this.cheese = new Cheese.Widget ();
+    this.cheese.set_vexpand (true);
+    this.cheese.set_hexpand (true);
+    this.cheese.set_no_show_all (true);
+    this.photobooth_page.attach (cheese, 0, 0);
+    this.photobooth_page.show ();
+
+    this.flash = new Cheese.Flash (this);
+#endif
+
+    this.views_stack.set_visible_child_name ("thumbnail-page");
+    /*
+    var remove_button = new ToolButton (null, null);
+    remove_button.set_icon_name ("list-remove-symbolic");
+    remove_button.get_style_context ().add_class (STYLE_CLASS_RAISED);
+    remove_button.is_important = true;
+    toolbar.add (remove_button);
+    remove_button.clicked.connect ( (button) => {
+       });
+    */
+
+    update_thumbnail_grid ();
+  }
+
+  private Gdk.Pixbuf scale_pixbuf_for_avatar_use (Gdk.Pixbuf pixbuf) {
     int w = pixbuf.get_width ();
     int h = pixbuf.get_height ();
 
@@ -60,12 +163,12 @@ public class Contacts.AvatarDialog : Dialog {
   }
 
   private ContactFrame create_frame (Gdk.Pixbuf source_pixbuf) {
-    var image_frame = new ContactFrame (icons_size, true);
-    var pixbuf = source_pixbuf.scale_simple (icons_size, icons_size, Gdk.InterpType.HYPER);
+    var image_frame = new ContactFrame (ICONS_SIZE, true);
+    var pixbuf = source_pixbuf.scale_simple (ICONS_SIZE, ICONS_SIZE, Gdk.InterpType.HYPER);
     image_frame.set_pixbuf (pixbuf);
     var avatar_pixbuf = scale_pixbuf_for_avatar_use (source_pixbuf);
     image_frame.clicked.connect ( () => {
-       selected_pixbuf (avatar_pixbuf);
+        selected_pixbuf (avatar_pixbuf);
       });
     return image_frame;
   }
@@ -97,55 +200,30 @@ public class Contacts.AvatarDialog : Dialog {
   }
 
   private void selected_pixbuf (Gdk.Pixbuf pixbuf) {
-    var p = pixbuf.scale_simple (main_size, main_size, Gdk.InterpType.HYPER);
-    main_frame.set_pixbuf (p);
+    var p = pixbuf.scale_simple (MAIN_SIZE, MAIN_SIZE, Gdk.InterpType.HYPER);
+    this.current_avatar.set_pixbuf (p);
 
-    new_pixbuf = pixbuf;
+    this.new_pixbuf = pixbuf;
     set_response_sensitive (ResponseType.OK, true);
   }
 
-  private void update_grid () {
-    int i = 0;
-    int j = 0;
-
-    if (contact != null) {
+  private void update_thumbnail_grid () {
+    if (this.contact != null) {
       foreach (var p in contact.individual.personas) {
-       ContactFrame? frame = frame_for_persona (p);
-       if (frame != null) {
-         view_grid.attach (frame, i, j, 1, 1);
-         i++;
-         if (i >= n_columns) {
-           i -= n_columns;
-           j++;
-         }
-       }
+        ContactFrame? frame = frame_for_persona (p);
+        if (frame != null)
+          this.thumbnail_grid.add (frame);
       }
     }
 
-    if (i != 0) {
-      i = 0;
-      j++;
-    }
-
-    if (j != 0) {
-      var s = new Separator (Orientation.HORIZONTAL);
-      view_grid.attach (s, 0, j++, n_columns, 1);
-    }
-
     var stock_files = Utils.get_stock_avatars ();
     foreach (var file_name in stock_files) {
       ContactFrame? frame = frame_for_filename (file_name);
-      if (frame != null) {
-       view_grid.attach (frame, i, j, 1, 1);
-       i++;
-       if (i >= n_columns) {
-         i -= n_columns;
-         j++;
-       }
-      }
+      if (frame != null)
+        this.thumbnail_grid.add (frame);
     }
 
-    view_grid.show_all ();
+    this.thumbnail_grid.show_all ();
   }
 
   public void update_preview (FileChooser chooser) {
@@ -157,51 +235,74 @@ public class Contacts.AvatarDialog : Dialog {
 
       var file = File.new_for_uri (uri);
       try {
-       var file_info = file.query_info (FileAttribute.STANDARD_CONTENT_TYPE,
-                                        FileQueryInfoFlags.NONE, null);
-       if (file_info != null) {
-         var mime_type = file_info.get_content_type ();
-
-         if (mime_type != null)
-           pixbuf = thumbnail_factory.generate_thumbnail (uri, mime_type);
-       }
+        var file_info = file.query_info (FileAttribute.STANDARD_CONTENT_TYPE,
+                         FileQueryInfoFlags.NONE, null);
+        if (file_info != null) {
+          var mime_type = file_info.get_content_type ();
+
+          if (mime_type != null)
+            pixbuf = thumbnail_factory.generate_thumbnail (uri, mime_type);
+        }
       } catch (GLib.Error e) {
       }
 
-      (chooser as Dialog).set_response_sensitive (ResponseType.ACCEPT,
-                                                 (pixbuf != null));
+      (chooser as Dialog).set_response_sensitive (ResponseType.ACCEPT, (pixbuf != null));
 
       if (pixbuf != null)
-       preview.set_from_pixbuf (pixbuf);
+        preview.set_from_pixbuf (pixbuf);
       else
-       preview.set_from_icon_name ("dialog-question",
-                                   IconSize.DIALOG);
+        preview.set_from_icon_name ("dialog-question", IconSize.DIALOG);
     }
 
     chooser.set_preview_widget_active (true);
   }
 
   private void set_crop_widget (Gdk.Pixbuf pixbuf) {
-    var frame_grid = views_stack.get_child_by_name ("crop-page") as Grid;
-    crop_area = new Um.CropArea ();
-    crop_area.set_vexpand (true);
-    crop_area.set_hexpand (true);
-    crop_area.set_min_size (48, 48);
-    crop_area.set_constrain_aspect (true);
-    crop_area.set_picture (pixbuf);
-
-    frame_grid.attach (crop_area, 0, 0, 1, 1);
-    frame_grid.show_all ();
-
-    views_stack.set_visible_child_name ("crop-page");
+    this.crop_area = new Um.CropArea ();
+    this.crop_area.set_vexpand (true);
+    this.crop_area.set_hexpand (true);
+    this.crop_area.set_min_size (48, 48);
+    this.crop_area.set_constrain_aspect (true);
+    this.crop_area.set_picture (pixbuf);
+
+    this.crop_page.attach (this.crop_area, 0, 0);
+    this.crop_page.show_all ();
+
+    this.views_stack.set_visible_child_name ("crop-page");
   }
 
-  private void select_avatar_file_cb () {
+  public override void response (int response_id) {
+    if (response_id == ResponseType.OK && this.new_pixbuf != null) {
+      try {
+        uint8[] buffer;
+        if (this.new_pixbuf.save_to_buffer (out buffer, "png", null)) {
+          var icon = new BytesIcon (new Bytes (buffer));
+          set_avatar (icon);
+        } else {
+          /* Failure. Fall through. */
+        }
+      } catch {
+      }
+    }
+
+#if HAVE_CHEESE
+    /* Ensure the Vala garbage collector disposes of the Cheese widget.
+     * This prevents the 'Device or resource busy' warnings, see:
+     *   https://bugzilla.gnome.org/show_bug.cgi?id=700959
+     */
+    this.cheese = null;
+#endif
+
+    this.destroy ();
+  }
+
+  [GtkCallback]
+  private void select_avatar_file_cb (Button button) {
     var chooser = new FileChooserDialog (_("Browse for more pictures"),
-                                        (Gtk.Window)this.get_toplevel (),
-                                        FileChooserAction.OPEN,
-                                        _("_Cancel"), ResponseType.CANCEL,
-                                        _("_Open"), ResponseType.ACCEPT);
+                                         (Gtk.Window)this.get_toplevel (),
+                                         FileChooserAction.OPEN,
+                                         _("_Cancel"), ResponseType.CANCEL,
+                                         _("_Open"), ResponseType.ACCEPT);
     chooser.set_modal (true);
     chooser.set_local_only (false);
     var preview = new Image ();
@@ -217,266 +318,72 @@ public class Contacts.AvatarDialog : Dialog {
       chooser.set_current_folder (folder);
 
     chooser.response.connect ( (response) => {
-       if (response != ResponseType.ACCEPT) {
-         chooser.destroy ();
-         return;
-       }
-       try {
-         var file = File.new_for_uri (chooser.get_uri ());
-         var in_stream = file.read ();
-         var pixbuf = new Gdk.Pixbuf.from_stream (in_stream, null);
-         in_stream.close ();
-         if (pixbuf.get_width () > 128 || pixbuf.get_height () > 128) {
-          set_crop_widget (pixbuf);
-         } else
-           selected_pixbuf (scale_pixbuf_for_avatar_use (pixbuf));
-
-         update_grid ();
-       } catch {
-       }
-
-       chooser.destroy ();
+        if (response != ResponseType.ACCEPT) {
+          chooser.destroy ();
+          return;
+        }
+        try {
+          var file = File.new_for_uri (chooser.get_uri ());
+          var in_stream = file.read ();
+          var pixbuf = new Gdk.Pixbuf.from_stream (in_stream, null);
+          in_stream.close ();
+          if (pixbuf.get_width () > 128 || pixbuf.get_height () > 128)
+            set_crop_widget (pixbuf);
+          else
+            selected_pixbuf (scale_pixbuf_for_avatar_use (pixbuf));
+
+          update_thumbnail_grid ();
+        } catch {
+        }
+
+        chooser.destroy ();
       });
 
     chooser.present ();
   }
 
-  public AvatarDialog (Contact? contact) {
-    Object (use_header_bar: 1);
-
-    thumbnail_factory = new Gnome.DesktopThumbnailFactory (Gnome.ThumbnailSize.NORMAL);
-    this.contact = contact;
-    set_title (_("Select Picture"));
-    set_transient_for (App.app.window);
-    set_modal (true);
-
-    var btn = add_button (_("Select"), ResponseType.OK);
-    btn.get_style_context ().add_class ("suggested-action");
-    add_button (_("Cancel"), ResponseType.CANCEL);
-
-    set_default_response (ResponseType.OK);
-    set_response_sensitive (ResponseType.OK, false);
-
-    var grid = new Grid ();
-    grid.set_border_width (8);
-    grid.set_column_spacing (16);
-    var container = (get_content_area () as Container);
-    container.add (grid);
-
-    main_frame = new ContactFrame (main_size);
-    if (contact != null) {
-      contact.keep_widget_uptodate (main_frame, (w) => {
-         (w as ContactFrame).set_image (contact.individual, contact);
-       });
-    } else {
-      main_frame.set_image (null, null);
-    }
-    main_frame.set_hexpand (false);
-    grid.attach (main_frame, 0, 0, 1, 1);
-
-    var label = new Label ("");
-    if (contact != null) {
-      label.set_markup (Markup.printf_escaped ("<span font='16'>%s</span>", contact.display_name));
-    } else {
-      label.set_markup (Markup.printf_escaped ("<span font='16'>%s</span>", _("New Contact")));
-    }
-    label.set_valign (Align.START);
-    label.set_halign (Align.START);
-    label.set_hexpand (true);
-    label.set_margin_top (4);
-    label.xalign = 0.0f;
-    label.set_ellipsize (Pango.EllipsizeMode.END);
-    grid.attach (label, 1, 0, 1, 1);
-
-    grid.set_row_spacing (11);
-
-    var frame = new Frame (null);
-    frame.get_style_context ().add_class ("contacts-avatar-frame");
-    grid.attach (frame, 0, 1, 2, 1);
-
-    views_stack = new Stack ();
-
-    frame.add (views_stack);
-
-    var frame_grid = new Grid ();
-    frame_grid.set_orientation (Orientation.VERTICAL);
-
-    /* main view */
-    var scrolled = new ScrolledWindow(null, null);
-    scrolled.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
-    scrolled.set_vexpand (true);
-    scrolled.set_hexpand (true);
-    scrolled.set_size_request (-1, 300);
-
-    frame_grid.add (scrolled);
-
-    view_grid = new Grid ();
-    scrolled.add (view_grid);
-
-    var actionbar = new ActionBar ();
-    frame_grid.add (actionbar);
-
-    var the_add_button = new Button.from_icon_name ("list-add-symbolic",
-                                                   IconSize.MENU);
-    the_add_button.clicked.connect (select_avatar_file_cb);
-
+  [GtkCallback]
+  private void on_webcam_button_clicked (Button button) {
 #if HAVE_CHEESE
-    var webcam_button = new Button.from_icon_name ("camera-photo-symbolic",
-                                                   IconSize.MENU);
-    webcam_button.sensitive = false;
-
-    camera_monitor = new Cheese.CameraDeviceMonitor ();
-    camera_monitor.added.connect ( () => {
-       num_cameras++;
-       webcam_button.sensitive = num_cameras > 0;
-    });
-    camera_monitor.removed.connect ( () => {
-       num_cameras--;
-       webcam_button.sensitive = num_cameras > 0;
-    });
-    camera_monitor.coldplug ();
-
-    webcam_button.clicked.connect ( (button) => {
-       views_stack.set_visible_child_name ("photobooth-page");
-       cheese.show ();
-      });
-
-    var bbox = new Box (Orientation.HORIZONTAL, 0);
-    bbox.get_style_context ().add_class ("linked");
-    bbox.add (the_add_button);
-    bbox.add (webcam_button);
-    actionbar.pack_start (bbox);
-
-#else
-    actionbar.pack_start (the_add_button);
+    this.views_stack.set_visible_child_name ("photobooth-page");
+    this.cheese.show ();
 #endif
+  }
 
-    frame_grid.show_all ();
-    views_stack.add_named (frame_grid, "thumbnail-factory");
-
-    /* crop page */
-    frame_grid = new Grid ();
-    frame_grid.set_orientation (Orientation.VERTICAL);
-
-    actionbar = new ActionBar ();
-    frame_grid.attach (actionbar, 0, 1, 1, 1);
-
-    var accept_button = new Button.from_icon_name ("object-select-symbolic",
-                                                  IconSize.MENU);
-    accept_button.clicked.connect ( (button) => {
-      var pix = crop_area.get_picture ();
-      selected_pixbuf (scale_pixbuf_for_avatar_use (pix));
-      crop_area.destroy ();
-      views_stack.set_visible_child_name ("thumbnail-factory");
-    });
-
-    var cancel_button = new Button.from_icon_name ("edit-undo-symbolic",
-                                                  IconSize.MENU);
-    cancel_button.clicked.connect ( (button) => {
-       crop_area.destroy ();
-       views_stack.set_visible_child_name ("thumbnail-factory");
-    });
-
-    var bbox1 = new Box (Orientation.HORIZONTAL, 0);
-    bbox1.get_style_context ().add_class ("linked");
-    bbox1.add (accept_button);
-    bbox1.add (cancel_button);
-    actionbar.pack_start (bbox1);
-
-    frame_grid.show_all ();
-    views_stack.add_named (frame_grid, "crop-page");
-
+  [GtkCallback]
+  private void on_photobooth_page_select_button_clicked (Button button) {
 #if HAVE_CHEESE
-    /* photobooth page */
-    frame_grid = new Grid ();
-    frame_grid.set_orientation (Orientation.VERTICAL);
-
-    cheese = new Cheese.Widget ();
-    cheese.set_vexpand (true);
-    cheese.set_hexpand (true);
-    cheese.set_no_show_all (true);
-    frame_grid.add (cheese);
-
-    flash = new Cheese.Flash (this);
-
-    actionbar = new ActionBar ();
-    frame_grid.attach (actionbar, 0, 1, 1, 1);
-
-    accept_button = new Button.from_icon_name ("object-select-symbolic",
-                                              IconSize.MENU);
-
-    accept_button.clicked.connect ( (button) => {
-       var camera = cheese.get_camera () as Cheese.Camera;
-
-        flash.fire ();
-
-       camera.photo_taken.connect ( (pix) => {
-           set_crop_widget (pix);
-           cheese.hide ();
-         });
-
-       if (!camera.take_photo_pixbuf ()) {
-           warning ("Unable to take photo");
-       }
+    var camera = this.cheese.get_camera () as Cheese.Camera;
+    this.flash.fire ();
+    camera.photo_taken.connect ( (pix) => {
+        set_crop_widget (pix);
+        this.cheese.hide ();
       });
 
-    cancel_button = new Button.from_icon_name ("edit-undo-symbolic",
-                                              IconSize.MENU);
-    cancel_button.clicked.connect ( (button) => {
-       views_stack.set_visible_child_name ("thumbnail-factory");
-       cheese.hide ();
-    });
-
-    bbox1 = new Box (Orientation.HORIZONTAL, 0);
-    bbox1.get_style_context ().add_class ("linked");
-    bbox1.add (accept_button);
-    bbox1.add (cancel_button);
-    actionbar.pack_start (bbox1);
-
-    frame_grid.show_all ();
-    views_stack.add_named (frame_grid, "photobooth-page");
+    if (!camera.take_photo_pixbuf ())
+      warning ("Unable to take photo");
 #endif
+  }
 
-    views_stack.set_visible_child_name ("thumbnail-factory");
-    /*
-    var remove_button = new ToolButton (null, null);
-    remove_button.set_icon_name ("list-remove-symbolic");
-    remove_button.get_style_context ().add_class (STYLE_CLASS_RAISED);
-    remove_button.is_important = true;
-    toolbar.add (remove_button);
-    remove_button.clicked.connect ( (button) => {
-               });
-    */
-
-    response.connect ( (response_id) => {
-       if (response_id == ResponseType.OK) {
-         if (new_pixbuf != null) {
-           try {
-             uint8[] buffer;
-             if (new_pixbuf.save_to_buffer (out buffer, "png", null)) {
-               var icon = new BytesIcon (new Bytes (buffer));
-               set_avatar (icon);
-             } else {
-               /* Failure. Fall through. */
-             }
-           } catch {
-           }
-         }
-       }
-
+  [GtkCallback]
+  private void on_photobooth_page_cancel_button_clicked (Button button) {
 #if HAVE_CHEESE
-       /* Ensure the Vala garbage collector disposes of the Cheese widget.
-        * This prevents the 'Device or resource busy' warnings, see:
-        *   https://bugzilla.gnome.org/show_bug.cgi?id=700959
-        */
-       cheese = null;
+    this.views_stack.set_visible_child_name ("thumbnail-page");
+    this.cheese.hide ();
 #endif
+  }
 
-       this.destroy ();
-      });
-
-    update_grid ();
+  [GtkCallback]
+  private void on_crop_page_select_button_clicked (Button button) {
+    var pix = crop_area.get_picture ();
+    selected_pixbuf (scale_pixbuf_for_avatar_use (pix));
+    this.crop_area.destroy ();
+    this.views_stack.set_visible_child_name ("thumbnail-page");
+  }
 
-    grid.show_all ();
+  [GtkCallback]
+  private void on_crop_page_cancel_button_clicked (Button button) {
+    this.crop_area.destroy ();
+    this.views_stack.set_visible_child_name ("thumbnail-page");
   }
 }



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