diff options
-rw-r--r-- | data/ui/contacts-avatar-dialog.ui | 313 | ||||
-rw-r--r-- | data/ui/style.css | 18 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/contacts-avatar-dialog.vala | 114 | ||||
-rw-r--r-- | src/contacts-contact-editor.vala | 32 | ||||
-rw-r--r-- | src/contacts-contact-frame.vala | 120 | ||||
-rw-r--r-- | src/contacts-utils.vala | 22 | ||||
-rw-r--r-- | src/meson.build | 2 |
8 files changed, 239 insertions, 383 deletions
diff --git a/data/ui/contacts-avatar-dialog.ui b/data/ui/contacts-avatar-dialog.ui index b9c0819..851116b 100644 --- a/data/ui/contacts-avatar-dialog.ui +++ b/data/ui/contacts-avatar-dialog.ui @@ -1,293 +1,164 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> <requires lib="gtk+" version="3.20"/> - <template class="ContactsAvatarDialog" parent="GtkDialog"> + <template class="ContactsAvatarPopover" parent="GtkPopover"> <property name="visible">True</property> - <property name="title" translatable="yes">Select Picture</property> - <property name="modal">True</property> <style> - <class name="contacts-avatar-dialog"/> + <class name="contacts-avatar-popover"/> </style> - <child internal-child="vbox"> + <child> <object class="GtkBox"> <property name="visible">True</property> <property name="orientation">vertical</property> + <property name="spacing">12</property> + <property name="margin">12</property> <child> - <object class="GtkGrid" id="grid"> + <object class="GtkStack" id="views_stack"> <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"> + <object class="GtkFlowBox" id="personas_thumbnail_grid"> <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> + <property name="orientation">vertical</property> </object> <packing> - <property name="top_attach">0</property> - <property name="left_attach">1</property> + <property name="name">thumbnail-page</property> </packing> </child> <child> - <object class="GtkFrame"> + <object class="GtkGrid" id="crop_page"> <property name="visible">True</property> - <style> - <class name="contacts-avatar-frame"/> - </style> + <property name="orientation">vertical</property> <child> - <object class="GtkStack" id="views_stack"> + <object class="GtkActionBar"> <property name="visible">True</property> <child> - <object class="GtkGrid" id="thumbnail_page"> + <object class="GtkBox"> <property name="visible">True</property> - <property name="orientation">vertical</property> + <property name="orientation">horizontal</property> + <style> + <class name="linked"/> + </style> <child> - <object class="GtkScrolledWindow"> + <object class="GtkButton"> <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> + <signal name="clicked" handler="on_crop_page_select_button_clicked" swapped="no"/> <child> - <object class="GtkBox"> + <object class="GtkImage"> <property name="visible">True</property> - <property name="orientation">vertical</property> - <child> - <object class="GtkFlowBox" id="personas_thumbnail_grid"> - <property name="visible">True</property> - </object> - </child> - <child> - <object class="GtkSeparator"> - <property name="visible">True</property> - <property name="orientation">horizontal</property> - </object> - </child> - <child> - <object class="GtkFlowBox" id="stock_thumbnail_grid"> - <property name="visible">True</property> - <property name="min_children_per_line">5</property> - <property name="max_children_per_line">8</property> - </object> - </child> + <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="GtkActionBar"> + <object class="GtkButton"> <property name="visible">True</property> + <signal name="clicked" handler="on_crop_page_cancel_button_clicked" swapped="no"/> <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 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> - <packing> - <property name="name">thumbnail-page</property> - </packing> </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="GtkGrid" id="crop_page"> + <object class="GtkBox"> <property name="visible">True</property> - <property name="orientation">vertical</property> + <property name="orientation">horizontal</property> + <style> + <class name="linked"/> + </style> <child> - <object class="GtkActionBar"> + <object class="GtkButton"> <property name="visible">True</property> + <signal name="clicked" handler="on_photobooth_page_select_button_clicked" swapped="no"/> <child> - <object class="GtkBox"> + <object class="GtkImage"> <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> + <property name="can_focus">False</property> + <property name="pixel_size">16</property> + <property name="icon_name">object-select-symbolic</property> </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"> + <object class="GtkButton"> <property name="visible">True</property> + <signal name="clicked" handler="on_photobooth_page_cancel_button_clicked" swapped="no"/> <child> - <object class="GtkBox"> + <object class="GtkImage"> <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> + <property name="can_focus">False</property> + <property name="pixel_size">16</property> + <property name="icon_name">edit-undo-symbolic</property> </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> + <packing> + <property name="top_attach">1</property> + <property name="left_attach">0</property> + </packing> </child> </object> <packing> - <property name="top_attach">1</property> - <property name="left_attach">0</property> - <property name="width">2</property> + <property name="name">photobooth-page</property> </packing> </child> </object> </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="orientation">horizontal</property> + <property name="halign">center</property> + <property name="spacing">12</property> + <child> + <object class="GtkButton" id="webcam_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label" translatable="yes">Take a picture…</property> + <signal name="clicked" handler="on_webcam_button_clicked" swapped="no"/> + </object> + </child> + <child> + <object class="GtkButton"> + <property name="visible">True</property> + <property name="label" translatable="yes">Select a file…</property> + <signal name="clicked" handler="select_avatar_file_cb" swapped="no"/> + </object> + </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 6a75863..01c94bb 100644 --- a/data/ui/style.css +++ b/data/ui/style.css @@ -56,23 +56,11 @@ row.contact-data-row { } .contacts-avatar-frame.frame { - border-width: 1px 1px 1px 1px; - border-style: solid; - border-color: @borders; - border-image: none; - border-radius: 0; - padding: 0; -} - -.main-avatar-frame.frame { - border-width: 1px; - border-style: solid; - border-color: @borders; - border-radius: 6px; + padding: 0; } .main-avatar-frame border { - border-radius: 5px; + border-width: 0; } /* Give the avatar in the ContactSheet some margin, @@ -136,6 +124,6 @@ ContactsWindow .primary-toolbar.toolbar { text-shadow: 1px 1px alpha(@theme_base_color, 0.6); } -.contacts-avatar-dialog .contact-display-name { +.contacts-avatar-popover .contact-display-name { font-size: 20px; } diff --git a/meson.build b/meson.build index 9238512..4f58e34 100644 --- a/meson.build +++ b/meson.build @@ -36,7 +36,6 @@ gee = dependency('gee-0.8') gio_unix = dependency('gio-unix-2.0', version: '>=' + min_glib_version) glib = dependency('glib-2.0', version: '>=' + min_glib_version) gmodule_export = dependency('gmodule-export-2.0', version: '>=' + min_glib_version) -gnome_desktop = dependency('gnome-desktop-3.0') goa = dependency('goa-1.0') gtk = dependency('gtk+-3.0', version: '>= 3.22.0') libebook = dependency('libebook-1.2', version: '>=' + min_eds_version) diff --git a/src/contacts-avatar-dialog.vala b/src/contacts-avatar-dialog.vala index 58f3010..21a0031 100644 --- a/src/contacts-avatar-dialog.vala +++ b/src/contacts-avatar-dialog.vala @@ -21,40 +21,32 @@ 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. + * This can be done by choosing from: + * - one of the contact's avatar, + * - an image file on the user's machine + * - (if cheese is enabled) a webcam. + * - a fallback avatar * * 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 { +public class Contacts.AvatarPopover : Popover { 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 personas_thumbnail_grid; [GtkChild] - private FlowBox stock_thumbnail_grid; - [GtkChild] private Grid crop_page; private Cc.CropArea crop_area; [GtkChild] private Grid photobooth_page; [GtkChild] private Button webcam_button; - [GtkChild] - private Box webcam_button_box; private ContactFrame current_avatar; @@ -72,13 +64,12 @@ public class Contacts.AvatarDialog : Dialog { */ public signal void set_avatar (GLib.Icon avatar_icon); - public AvatarDialog (Window main_window, Contact? contact) { + public AvatarPopover (Widget? relative_to, Contact? contact) { Object ( - transient_for: main_window, - use_header_bar: 1 + relative_to: relative_to, + modal: true ); - this.thumbnail_factory = new Gnome.DesktopThumbnailFactory (Gnome.ThumbnailSize.NORMAL); this.contact = contact; // Load the current avatar @@ -92,14 +83,10 @@ public class Contacts.AvatarDialog : Dialog { } this.current_avatar.set_hexpand (false); this.current_avatar.show (); - this.grid.attach (this.current_avatar, 0, 0); + /* 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 ( () => { @@ -125,9 +112,6 @@ public class Contacts.AvatarDialog : Dialog { this.photobooth_page.show (); this.flash = new Cheese.Flash (this); -#else - // Don't show the camera button - this.webcam_button_box.hide (); #endif this.views_stack.set_visible_child_name ("thumbnail-page"); @@ -141,7 +125,7 @@ public class Contacts.AvatarDialog : Dialog { }); */ - update_thumbnail_grids (); + update_thumbnail_grid (); } private Gdk.Pixbuf scale_pixbuf_for_avatar_use (Gdk.Pixbuf pixbuf) { @@ -189,25 +173,14 @@ public class Contacts.AvatarDialog : Dialog { 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) { var p = pixbuf.scale_simple (MAIN_SIZE, MAIN_SIZE, Gdk.InterpType.HYPER); this.current_avatar.set_pixbuf (p); this.new_pixbuf = pixbuf; - set_response_sensitive (ResponseType.OK, true); } - private void update_thumbnail_grids () { + private void update_thumbnail_grid () { if (this.contact != null) { foreach (var p in contact.individual.personas) { ContactFrame? frame = frame_for_persona (p); @@ -216,14 +189,6 @@ public class Contacts.AvatarDialog : Dialog { } } this.personas_thumbnail_grid.show_all (); - - var stock_files = Utils.get_stock_avatars (); - foreach (var file_name in stock_files) { - ContactFrame? frame = frame_for_filename (file_name); - if (frame != null) - this.stock_thumbnail_grid.add (frame); - } - this.stock_thumbnail_grid.show_all (); } public void update_preview (FileChooser chooser) { @@ -240,8 +205,9 @@ public class Contacts.AvatarDialog : Dialog { if (file_info != null) { var mime_type = file_info.get_content_type (); - if (mime_type != null) - pixbuf = thumbnail_factory.generate_thumbnail (uri, mime_type); + //XXX FIXME do this without gnome-desktop pls + /* if (mime_type != null) */ + /* pixbuf = thumbnail_factory.generate_thumbnail (uri, mime_type); */ } } catch (GLib.Error e) { } @@ -271,30 +237,30 @@ public class Contacts.AvatarDialog : Dialog { this.views_stack.set_visible_child_name ("crop-page"); } - 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 (); - } + /* 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) { @@ -332,7 +298,7 @@ public class Contacts.AvatarDialog : Dialog { else selected_pixbuf (scale_pixbuf_for_avatar_use (pixbuf)); - update_thumbnail_grids (); + update_thumbnail_grid (); } catch { } diff --git a/src/contacts-contact-editor.vala b/src/contacts-contact-editor.vala index b94dba5..72626c7 100644 --- a/src/contacts-contact-editor.vala +++ b/src/contacts-contact-editor.vala @@ -978,23 +978,23 @@ public class Contacts.ContactEditor : Grid { this.container_grid.attach (this.avatar_frame, 0, 0, 1, 3); } - // Show the avatar dialog when the avatar is clicked + // Show the avatar popover when the avatar is clicked private void on_avatar_frame_clicked () { - var dialog = new AvatarDialog ((Window) get_toplevel (), this.contact); - dialog.set_avatar.connect ( (icon) => { - this.avatar_frame.set_data ("value", icon); - this.avatar_frame.set_data ("changed", true); - - Gdk.Pixbuf? a_pixbuf = null; - try { - var stream = (icon as LoadableIcon).load (PROFILE_SIZE, null); - a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, PROFILE_SIZE, PROFILE_SIZE, true); - } catch { - } - - this.avatar_frame.set_pixbuf (a_pixbuf); - }); - dialog.run (); + var popover = new AvatarPopover (this.avatar_frame, this.contact); + /* dialog.set_avatar.connect ( (icon) => { */ + /* this.avatar_frame.set_data ("value", icon); */ + /* this.avatar_frame.set_data ("changed", true); */ + + /* Gdk.Pixbuf? a_pixbuf = null; */ + /* try { */ + /* var stream = (icon as LoadableIcon).load (PROFILE_SIZE, null); */ + /* a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, PROFILE_SIZE, PROFILE_SIZE, true); */ + /* } catch { */ + /* } */ + + /* this.avatar_frame.set_pixbuf (a_pixbuf); */ + /* }); */ + popover.show (); } public bool avatar_changed () { diff --git a/src/contacts-contact-frame.vala b/src/contacts-contact-frame.vala index b1ec072..c216d63 100644 --- a/src/contacts-contact-frame.vala +++ b/src/contacts-contact-frame.vala @@ -23,15 +23,17 @@ using Gee; public class Contacts.ContactFrame : Frame { private int size; private Gdk.Pixbuf? pixbuf; - private Pango.Layout? layout; + private Contact? contact; public signal void clicked (); public ContactFrame (int size, bool with_button = false) { this.size = size; + this.contact = null; - var image = new Image (); + var image = new DrawingArea (); image.set_size_request (size, size); + //TODO Border (or not? what if we have a color) if (with_button) { var button = new Button (); @@ -51,61 +53,115 @@ public class Contacts.ContactFrame : Frame { } image.show (); - image.draw.connect (draw_image); + image.draw.connect (on_draw_avatar); set_shadow_type (ShadowType.NONE); } - public void set_pixbuf (Gdk.Pixbuf a_pixbuf) { - pixbuf = Contact.frame_icon (a_pixbuf); + public void set_pixbuf (Gdk.Pixbuf? a_pixbuf) { + this.pixbuf = (a_pixbuf != null)? Contact.frame_icon (a_pixbuf) : null; queue_draw (); } public void set_image (AvatarDetails? details, Contact? contact = null) { + this.contact = contact; + Gdk.Pixbuf? a_pixbuf = null; - if (details != null && - details.avatar != null) { + if (details != null && details.avatar != null) { try { - var stream = details.avatar.load (size, null); - a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true); + var stream = details.avatar.load (size, null); + a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true); } catch { } } if (a_pixbuf == null) { - a_pixbuf = Contact.draw_fallback_avatar (size, contact); + a_pixbuf = null; } set_pixbuf (a_pixbuf); } - public bool draw_image (Cairo.Context cr) { + public bool on_draw_avatar (Cairo.Context cr) { cr.save (); - if (pixbuf != null) { - Gdk.cairo_set_source_pixbuf (cr, pixbuf, 0, 0); - cr.paint(); - } + if (this.pixbuf != null) + draw_pixbuf (cr); + else // Draw the standard person fallback + draw_initial (cr); - if (layout != null) { - Utils.cairo_rounded_box (cr, 0, 0, size, size, 4); - cr.clip (); - - cr.set_source_rgba (0, 0, 0, 0.5); - cr.rectangle (0, size, size, 0); - cr.fill (); - - cr.set_source_rgb (1.0, 1.0, 1.0); - Pango.Rectangle rect; - layout.get_extents (null, out rect); - double label_width = rect.width/(double)Pango.SCALE; - double label_height = rect.height / (double)Pango.SCALE; - cr.move_to (Math.round ((size - label_width) / 2.0), - size + Math.floor ((-label_height) / 2.0)); - Pango.cairo_show_layout (cr, layout); - } cr.restore (); return true; } + + private void draw_pixbuf (Cairo.Context cr) { + Gdk.cairo_set_source_pixbuf (cr, this.pixbuf, 0, 0); + // Clip with a circle + cr.arc (this.size / 2, this.size / 2, (this.size - 1) / 2, 0, 2*Math.PI); + cr.clip_preserve (); + cr.paint (); + + // Draw a border + cr.arc (this.size / 2, this.size / 2, (this.size - 1) / 2, 0, 2*Math.PI); + cr.set_line_width (0.5); + cr.set_source_rgb (0, 0, 0); + cr.stroke (); + } + + private void draw_initial (Cairo.Context cr) { + // The background colors + double bg_r, bg_g, bg_b; + get_background_color (out bg_r, out bg_g, out bg_b); + // The foreground colors + var fg_r = bg_r * 0.5; + var fg_g = bg_g * 0.5; + var fg_b = bg_b * 0.5; + + // Draw the background circle + cr.set_source_rgb (bg_r, bg_g, bg_b); + cr.arc (this.size / 2, this.size / 2, (this.size - 1) / 2, 0, 2*Math.PI); + cr.fill_preserve (); + // Draw the border + cr.set_line_width (0.5); + cr.set_source_rgb (fg_r, fg_g, fg_b); + cr.stroke (); + + // Draw the initial + if (this.contact != null && this.contact.display_name != "") { + var initial = this.contact.display_name.get_char_validated (); + if (initial == -1) + return; + var initial_upper = initial.totitle ().to_string (); + + // Get the styling right + cr.set_source_rgb (fg_r, fg_g, fg_b); + //XXX no better way to do this (ie without hardcoding Cantarell) + cr.select_font_face ("Cantarell", Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL); + cr.set_font_size (this.size * 0.66); + // Center it + Cairo.TextExtents extents; + cr.text_extents (initial_upper, out extents); + cr.move_to ((this.size - extents.width) / 2 - extents.x_bearing, + (this.size - extents.height) / 2 - extents.y_bearing); + + cr.show_text (initial_upper); + } + } + + private void get_background_color (out double r, out double g, out double b) { + //XXX find something if this.contact == nulll or id == "" + + // We use the hash of the id so we get the same color for a contact + var hash = str_hash (this.contact.individual.id); + + r = ((hash & 0xFF0000) >> 16) / 255.0; + g = ((hash & 0x00FF00) >> 8) / 255.0; + b = (hash & 0x0000FF) / 255.0; + + // Make it a bit lighter by default (and since the foreground will be darker) + r = (r + 2) / 3.0; + g = (g + 2) / 3.0; + b = (b + 2) / 3.0; + } } diff --git a/src/contacts-utils.vala b/src/contacts-utils.vala index 954e1f2..b828467 100644 --- a/src/contacts-utils.vala +++ b/src/contacts-utils.vala @@ -273,28 +273,6 @@ namespace Contacts.Utils { entry.select_region (start, end); } - public 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 (Error e) { - debug ("Couldn't open stock avatars folder \"%s\": %s", path, e.message); - } - if (dir != null) { - string? face; - while ((face = dir.read_name ()) != null) { - var filename = Path.build_filename (path, face); - files += filename; - } - } - }; - return files; - } - public PersonaStore[] get_eds_address_books (Store contacts_store) { PersonaStore[] stores = {}; foreach (var backend in contacts_store.backend_store.enabled_backends.values) { diff --git a/src/meson.build b/src/meson.build index 2a387c3..f8067be 100644 --- a/src/meson.build +++ b/src/meson.build @@ -42,7 +42,6 @@ contacts_c_sources = [ contacts_c_args = [ '-include', 'config.h', - '-DGNOME_DESKTOP_USE_UNSTABLE_API', '-DLOCALEDIR="@0@"'.format(locale_dir), ] @@ -52,7 +51,6 @@ contacts_deps = [ gee, gio_unix, glib, - gnome_desktop, goa, gtk, libebook, |