[gnome-contacts] Use a dialog for avatar changing



commit 934fb57707d4e533e80dc13af12ec398acc52c66
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Feb 2 20:09:49 2012 +0100

    Use a dialog for avatar changing
    
    This is according to the new mockup

 po/POTFILES.in                  |    2 +-
 src/Makefile.am                 |    3 +-
 src/contacts-avatar-dialog.vala |  301 +++++++++++++++++++++++++++++++++++++++
 src/contacts-avatar-menu.vala   |  193 -------------------------
 src/contacts-contact-frame.vala |  157 +++-----------------
 src/contacts-contact-pane.vala  |   44 ++++---
 src/contacts-utils.vala         |   21 +++
 src/memory-icon.vala            |   88 ++++++++++++
 8 files changed, 461 insertions(+), 348 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6ba3db8..2ab8b5f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,7 +1,7 @@
 data/gnome-contacts.desktop.in.in
 [type: gettext/glade]src/app-menu.ui
 src/contacts-app.vala
-src/contacts-avatar-menu.vala
+src/contacts-avatar-dialog.vala
 src/contacts-contact-pane.vala
 src/contacts-contact.vala
 src/contacts-esd-setup.c
diff --git a/src/Makefile.am b/src/Makefile.am
index d6ff529..cbb0e73 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,12 +32,13 @@ vala_sources = \
 	contacts-utils.vala \
 	contacts-clickable.vala \
 	contacts-new-contact-dialog.vala \
-	contacts-avatar-menu.vala \
+	contacts-avatar-dialog.vala \
 	contacts-contact-frame.vala \
 	contacts-revealer.vala \
 	contacts-setup-window.vala \
 	contacts-window.vala \
 	main.vala \
+	memory-icon.vala \
 	$(NULL)
 
 gsettingsschema_in_files = org.gnome.Contacts.gschema.xml.in
diff --git a/src/contacts-avatar-dialog.vala b/src/contacts-avatar-dialog.vala
new file mode 100644
index 0000000..5d0355b
--- /dev/null
+++ b/src/contacts-avatar-dialog.vala
@@ -0,0 +1,301 @@
+/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2011 Alexander Larsson <alexl redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+using Gtk;
+using Folks;
+
+public class Contacts.AvatarDialog : Dialog {
+  private Gnome.DesktopThumbnailFactory thumbnail_factory;
+  const int main_size = 96;
+  const int icons_size = 64;
+  private Contact contact;
+  private Grid view_grid;
+  private ContactFrame main_frame;
+
+  private Gdk.Pixbuf? new_pixbuf;
+
+  public signal void set_avatar (GLib.Icon avatar_icon);
+
+  Gdk.Pixbuf scale_pixbuf_for_avatar_use (Gdk.Pixbuf pixbuf) {
+    int w = pixbuf.get_width ();
+    int h = pixbuf.get_height ();
+
+    if (w <= 128 && h <= 128)
+      return pixbuf;
+
+    if (w > h) {
+      h = (int)Math.round (h * 128.0 / w);
+      w = 128;
+    } else {
+      w = (int)Math.round (w * 128.0 / h);
+      h = 128;
+    }
+
+    return pixbuf.scale_simple (w, h, Gdk.InterpType.HYPER);
+  }
+
+  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);
+    image_frame.set_pixbuf (pixbuf);
+    var avatar_pixbuf = scale_pixbuf_for_avatar_use (source_pixbuf);
+    image_frame.clicked.connect ( () => {
+	selected_pixbuf (avatar_pixbuf);
+      });
+    return image_frame;
+  }
+
+  private ContactFrame? frame_for_persona (Persona persona) {
+    var details = persona as AvatarDetails;
+    if (details == null || details.avatar == null)
+      return null;
+
+    try {
+      var stream = details.avatar.load (128, null);
+      var pixbuf = new Gdk.Pixbuf.from_stream (stream);
+      return create_frame (pixbuf);
+    }
+    catch {
+    }
+
+    return null;
+  }
+
+  private ContactFrame? frame_for_filename (string filename) {
+    ContactFrame? image_frame = null;
+    try {
+      var pixbuf = new Gdk.Pixbuf.from_file (filename);
+      return create_frame (pixbuf);
+    } catch {
+    }
+    return image_frame;
+  }
+
+  private void selected_pixbuf (Gdk.Pixbuf pixbuf) {
+    try {
+      var p = pixbuf.scale_simple (main_size, main_size, Gdk.InterpType.HYPER);
+      main_frame.set_pixbuf (p);
+
+      new_pixbuf = pixbuf;
+    } catch {
+    }
+  }
+
+  private void update_grid () {
+    int i = 0;
+    int j = 0;
+
+    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 >= 4) {
+	  i -= 4;
+	  j++;
+	}
+      }
+    }
+
+    if (i != 0) {
+      i = 0;
+      j++;
+    }
+
+    if (j != 0) {
+      var s = new Separator (Orientation.HORIZONTAL);
+      view_grid.attach (s, 0, j++, 4, 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 >= 4) {
+	  i -= 4;
+	  j++;
+	}
+      }
+    }
+
+    view_grid.show_all ();
+  }
+
+  public void update_preview (FileChooser chooser) {
+    var uri = chooser.get_preview_uri ();
+    if (uri != null) {
+      Gdk.Pixbuf? pixbuf = null;
+
+      var preview = chooser.get_preview_widget () as Image;
+
+      var file = File.new_for_uri (uri);
+      try {
+	var file_info = file.query_info (GLib.FILE_ATTRIBUTE_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));
+
+      if (pixbuf != null)
+	preview.set_from_pixbuf (pixbuf);
+      else
+	preview.set_from_stock (Stock.DIALOG_QUESTION,
+				IconSize.DIALOG);
+    }
+
+    chooser.set_preview_widget_active (true);
+  }
+
+  private void select_avatar_file_cb () {
+    var chooser = new FileChooserDialog (_("Browse for more pictures"),
+					 (Window)this.get_toplevel (),
+					 FileChooserAction.OPEN,
+					 Stock.CANCEL, ResponseType.CANCEL,
+					 Stock.OPEN, ResponseType.ACCEPT);
+    chooser.set_modal (true);
+    chooser.set_local_only (false);
+    var preview = new Image ();
+    preview.set_size_request (128, -1);
+    chooser.set_preview_widget (preview);
+    chooser.set_use_preview_label (false);
+    preview.show ();
+
+    chooser.update_preview.connect (update_preview);
+
+    var folder = Environment.get_user_special_dir (UserDirectory.PICTURES);
+    if (folder != null)
+      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 ();
+	  selected_pixbuf (scale_pixbuf_for_avatar_use (pixbuf));
+	} catch {
+	}
+
+	chooser.destroy ();
+      });
+
+    chooser.present ();
+  }
+
+
+  public AvatarDialog (Contact contact) {
+    thumbnail_factory = new Gnome.DesktopThumbnailFactory (Gnome.ThumbnailSize.NORMAL);
+    this.contact = contact;
+    set_title (_("Select Picture"));
+    set_transient_for (App.app.window);
+    set_modal (true);
+    add_buttons (_("Close"), ResponseType.CLOSE, null);
+
+    var grid = new Grid ();
+    grid.set_border_width (8);
+    grid.set_column_spacing (8);
+    var container = (get_content_area () as Container);
+    container.add (grid);
+
+    main_frame = new ContactFrame (main_size);
+    contact.keep_widget_uptodate (main_frame, (w) => {
+	(w as ContactFrame).set_image (contact.individual, contact);
+      });
+    main_frame.set_hexpand (false);
+    grid.attach (main_frame, 0, 0, 1, 1);
+
+    var label = new Label ("");
+    label.set_markup ("<span font='13'>" + contact.display_name + "</span>");
+    label.set_valign (Align.START);
+    label.set_halign (Align.START);
+    label.set_hexpand (true);
+    label.xalign = 0.0f;
+    label.set_ellipsize (Pango.EllipsizeMode.END);
+    grid.attach (label, 1, 0, 1, 1);
+
+    grid.set_row_spacing (18);
+
+    var frame = new Frame (null);
+    grid.attach (frame, 0, 1, 2, 1);
+    var frame_grid = new Grid ();
+    frame_grid.set_orientation (Orientation.VERTICAL);
+    frame.add (frame_grid);
+
+    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_with_viewport (view_grid);
+
+    var toolbar = new Toolbar ();
+    toolbar.get_style_context ().add_class (STYLE_CLASS_PRIMARY_TOOLBAR);
+    toolbar.set_icon_size (IconSize.MENU);
+    toolbar.set_vexpand (false);
+    frame_grid.add (toolbar);
+
+    var add_button = new ToolButton (null, null);
+    add_button.set_icon_name ("list-add-symbolic");
+    add_button.get_style_context ().add_class (STYLE_CLASS_RAISED);
+    add_button.is_important = true;
+    toolbar.add (add_button);
+    add_button.clicked.connect (select_avatar_file_cb);
+
+    /*
+    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.CLOSE) {
+	  if (new_pixbuf != null) {
+	    var icon = new MemoryIcon.from_pixbuf (new_pixbuf);
+	    set_avatar (icon);
+	  }
+	}
+	this.destroy ();
+      });
+
+    update_grid ();
+
+    grid.show_all ();
+  }
+}
diff --git a/src/contacts-contact-frame.vala b/src/contacts-contact-frame.vala
index 0c4bc82..59dfe09 100644
--- a/src/contacts-contact-frame.vala
+++ b/src/contacts-contact-frame.vala
@@ -26,143 +26,28 @@ public class Contacts.ContactFrame : Frame {
   private Gdk.Pixbuf? pixbuf;
   private Pango.Layout? layout;
   private int text_height;
-  private bool popup_in_progress;
-  private Gtk.Menu? menu;
 
-  private void menu_position (Gtk.Menu menu, out int x, out int y, out bool push_in) {
-    Allocation allocation;
-    get_allocation (out allocation);
+  public signal void clicked ();
 
-    int sx = 0;
-    int sy = 0;
-
-    if (!get_has_window ()) {
-      sx += allocation.x;
-      sy += allocation.y;
-    }
-
-    get_window ().get_root_coords (sx, sy, out sx, out sy);
-
-    Requisition menu_req;
-    Gdk.Rectangle monitor;
-
-    menu.get_preferred_size (null, out menu_req);
-
-    if (get_direction () == TextDirection.LTR)
-      x = sx + 2;
-    else
-      x = sx + allocation.width - menu_req.width - 2;
-    y = sy - 2;
-
-    var window = get_window ();
-    var screen = get_screen ();
-    var monitor_num = screen.get_monitor_at_window (window);
-    if (monitor_num < 0)
-      monitor_num = 0;
-    screen.get_monitor_geometry (monitor_num, out monitor);
-
-    if (x < monitor.x)
-      x = monitor.x;
-    else if (x + menu_req.width > monitor.x + monitor.width)
-      x = monitor.x + monitor.width - menu_req.width;
-
-    if (monitor.y + monitor.height - y - allocation.height >= menu_req.height)
-      y += allocation.height;
-    else if (y - monitor.y >= menu_req.height)
-      y -= menu_req.height;
-    else if (monitor.y + monitor.height - y - allocation.height > y - monitor.y)
-      y += allocation.height;
-    else
-      y -= menu_req.height;
-
-    menu.set_monitor (monitor_num);
-
-    Window? toplevel = menu.get_parent() as Window;
-    if (toplevel != null && !toplevel.get_visible())
-      toplevel.set_type_hint (Gdk.WindowTypeHint.DROPDOWN_MENU);
-
-    push_in = false;
-  }
-
-  public ContactFrame (int size, Gtk.Menu? menu = null) {
+  public ContactFrame (int size, bool with_button = false) {
     this.size = size;
 
     var image = new Image ();
     image.set_size_request (size, size);
 
-    this.menu = menu;
-
-    var button = new ToggleButton ();
-    button.set_focus_on_click (false);
-    button.get_style_context ().add_class ("contacts-frame-button");
-    button.add (image);
-    button.set_mode (false);
-    this.add (button);
-
-    button.toggled.connect ( () => {
-	if (this.menu == null) {
-	  if (button.get_active ())
-	    button.set_active (false);
-	  return;
-	}
-
-	if (button.get_active ()) {
-	  if (!popup_in_progress) {
-	    menu.popup (null, null, menu_position, 1, Gtk.get_current_event_time ());
-	  }
-	} else {
-	  menu.popdown ();
-	}
-      });
-
-    button.button_press_event.connect ( (event) => {
-	if (this.menu == null)
-	  return true;
-	var ewidget = Gtk.get_event_widget ((Gdk.Event)(&event));
-
-	if (ewidget != button ||
-	    button.get_active ())
-	  return false;
-
-	menu.popup (null, null, menu_position, 1, Gtk.get_current_event_time ());
-	button.set_active (true);
-	popup_in_progress = true;
-	return true;
-      });
-
-    button.button_release_event.connect ( (event) => {
-	if (this.menu == null)
-	  return false;
-
-	bool popup_in_progress_saved = popup_in_progress;
-	popup_in_progress = false;
-
-	var ewidget = Gtk.get_event_widget ((Gdk.Event)(&event));
-
-	if (ewidget == button &&
-	    !popup_in_progress_saved &&
-	    button.get_active ()) {
-	  menu.popdown ();
-	  return true;
-	}
-	if (ewidget != button)    {
-	  menu.popdown ();
-	  return true;
-	}
-	return false;
-      });
-
-    if (menu != null) {
-      menu.show.connect ( (menu) => {
-	  popup_in_progress = true;
-	  button.set_active (true);
-	  popup_in_progress = false;
-	});
-      menu.hide.connect ( (menu) => {
-	  button.set_active (false);
-	});
-      menu.attach_to_widget (button, (menu) => {
+    if (with_button) {
+      var button = new Button ();
+      button.set_relief (ReliefStyle.NONE);
+      button.set_focus_on_click (false);
+      button.add (image);
+
+      button.clicked.connect ( () => {
+	  this.clicked ();
 	});
+
+      this.add (button);
+    } else {
+      this.add (image);
     }
 
     image.show ();
@@ -171,23 +56,27 @@ public class Contacts.ContactFrame : Frame {
     set_shadow_type (ShadowType.NONE);
   }
 
+  public void set_pixbuf (Gdk.Pixbuf a_pixbuf) {
+    pixbuf = Contact.frame_icon (a_pixbuf);
+    queue_draw ();
+  }
+
   public void set_image (AvatarDetails? details, Contact? contact = null) {
-    pixbuf = null;
+    Gdk.Pixbuf? a_pixbuf = null;
     if (details != null &&
 	details.avatar != null) {
       try {
 	var stream = details.avatar.load (size, null);
-	pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true);
+	a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true);
       }
       catch {
       }
     }
 
-    if (pixbuf == null) {
-      pixbuf = Contact.draw_fallback_avatar (size, contact);
+    if (a_pixbuf == null) {
+      a_pixbuf = Contact.draw_fallback_avatar (size, contact);
     }
-    pixbuf = Contact.frame_icon (pixbuf);
-    queue_draw ();
+    set_pixbuf (a_pixbuf);
   }
 
   public void set_text (string? text_, int text_height_) {
diff --git a/src/contacts-contact-pane.vala b/src/contacts-contact-pane.vala
index 022c84e..96fb247 100644
--- a/src/contacts-contact-pane.vala
+++ b/src/contacts-contact-pane.vala
@@ -1482,6 +1482,25 @@ public class Contacts.ContactPane : ScrolledWindow {
     return null;
   }
 
+  private void change_avatar (ContactFrame image_frame) {
+    var dialog = new AvatarDialog (contact);
+    dialog.show_all ();
+    dialog.set_avatar.connect ( (icon) =>  {
+	Value v = Value (icon.get_type ());
+	v.set_object (icon);
+	set_individual_property.begin (contact,
+				       "avatar", v,
+				       (obj, result) => {
+					 try {
+					   var p = set_individual_property.end (result);
+					 } catch (Error e) {
+					   App.app.show_message (e.message);
+					   image_frame.set_image (contact.individual, contact);
+					 }
+				       });
+      });
+  }
+
   public void update_card () {
     foreach (var w in card_grid.get_children ()) {
       w.destroy ();
@@ -1490,23 +1509,10 @@ public class Contacts.ContactPane : ScrolledWindow {
     if (contact == null)
       return;
 
-    var menu = new AvatarMenu (contact);
-    var image_frame = new ContactFrame (PROFILE_SIZE, menu);
-    menu.icon_set.connect ( (icon) => {
-	Value v = Value (icon.get_type ());
-	v.set_object (icon);
-	set_individual_property.begin (contact,
-				       "avatar", v,
-					 (obj, result) => {
-					   try {
-					     var p = set_individual_property.end (result);
-					   } catch (Error e) {
-					     App.app.show_message (e.message);
-					     image_frame.set_image (contact.individual, contact);
-					   }
-				       });
+    var image_frame = new ContactFrame (PROFILE_SIZE, true);
+    image_frame.clicked.connect ( () => {
+	change_avatar (image_frame);
       });
-
     contact.keep_widget_uptodate (image_frame,  (w) => {
 	(w as ContactFrame).set_image (contact.individual, contact);
       });
@@ -1708,7 +1714,7 @@ public class Contacts.ContactPane : ScrolledWindow {
   }
 
   public signal void contacts_linked (string main_contact, string linked_contact, LinkOperation operation);
-  
+
   public void add_suggestion (Contact c) {
     var row = new FieldRow (row_group);
     personas_grid.add (row);
@@ -1756,8 +1762,8 @@ public class Contacts.ContactPane : ScrolledWindow {
       var main_contact = contact.display_name;
       var linked_contact = c.display_name;
       link_contacts.begin (contact, c, (obj, result) => {
-        var operation = link_contacts.end (result);
-        this.contacts_linked (main_contact, linked_contact, operation);
+	var operation = link_contacts.end (result);
+	this.contacts_linked (main_contact, linked_contact, operation);
       });
       row.destroy ();
     });
diff --git a/src/contacts-utils.vala b/src/contacts-utils.vala
index 774ddf9..6c5b7db 100644
--- a/src/contacts-utils.vala
+++ b/src/contacts-utils.vala
@@ -242,4 +242,25 @@ public class Contacts.Utils : Object {
       spawn_app (calendar_settings);
     }
   }
+
+  public static string[] get_stock_avatars () {
+    string[] files = {};
+    var system_data_dirs = Environment.get_system_data_dirs ();
+    foreach (var data_dir in system_data_dirs) {
+      var path = Path.build_filename (data_dir, "pixmaps", "faces");
+      Dir? dir = null;
+      try {
+	dir = Dir.open (path);
+      }	catch {
+      }
+      if (dir != null) {
+	string? face;
+	while ((face = dir.read_name ()) != null) {
+	  var filename = Path.build_filename (path, face);
+	  files += filename;
+	}
+      }
+    };
+    return files;
+  }
 }
diff --git a/src/memory-icon.vala b/src/memory-icon.vala
new file mode 100644
index 0000000..febb700
--- /dev/null
+++ b/src/memory-icon.vala
@@ -0,0 +1,88 @@
+/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2011 Philip Withnall
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *       Philip Withnall <philip tecnocode co uk>
+ */
+
+using GLib;
+
+/**
+ * A wrapper around a blob of image data (with an associated content type) which
+ * presents it as a { link GLib.LoadableIcon}. This allows inlined avatars to be
+ * returned as { link GLib.LoadableIcon}s.
+ */
+internal class Contacts.MemoryIcon : Object, Icon, LoadableIcon {
+  private uint8[] _image_data;
+  private string? _image_type;
+
+  public MemoryIcon (string? image_type, uint8[] image_data) {
+    this._image_data = image_data;
+    this._image_type = image_type;
+  }
+
+  public MemoryIcon.from_pixbuf (Gdk.Pixbuf pixbuf) throws GLib.Error {
+    uint8[] buffer;
+    if (pixbuf.save_to_buffer (out buffer, "png", null)) {
+      this ("image/png", buffer);
+    }
+  }
+
+#if VALA_0_16
+  public bool equal (Icon? icon2)
+#else
+  public bool equal (Icon icon2)
+#endif
+  {
+    /* These type and nullability checks are taken care of by the interface
+     * wrapper. */
+    var icon = (MemoryIcon) (!) icon2;
+    return (this._image_data.length == icon._image_data.length &&
+	    Memory.cmp (this._image_data, icon._image_data,
+			this._image_data.length) == 0);
+  }
+
+  public uint hash () {
+    /* Implementation based on g_str_hash() from GLib. We initialise the hash
+     * with the g_str_hash() hash of the image type (which itself is
+     * initialised with the magic number in GLib thought up by cleverer people
+     * than myself), then add each byte in the image data to the hash value
+     * by multiplying the hash value by 33 and adding the image data, as is
+     * done on all bytes in g_str_hash(). I leave the rationale for this
+     * calculation to the author of g_str_hash().
+     *
+     * Basically, this is just a nul-safe version of g_str_hash(). Which is
+     * calculated over both the image type and image data. */
+    uint hash = this._image_type != null ? ((!) this._image_type).hash () : 0;
+    for (uint i = 0; i < this._image_data.length; i++) {
+      hash = (hash << 5) + hash + this._image_data[i];
+    }
+
+    return hash;
+  }
+
+  public InputStream load (int size, out string? type,
+			   Cancellable? cancellable = null) {
+    type = this._image_type;
+    return new MemoryInputStream.from_data (this._image_data, free);
+  }
+
+  public async InputStream load_async (int size, GLib.Cancellable? cancellable, out string? type) {
+    type = this._image_type;
+    return new MemoryInputStream.from_data (this._image_data, free);
+  }
+}



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