2 * Copyright (C) 2010 Collabora Ltd.
4 * This library is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 2.1 of the License, or
7 * (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
18 * Travis Reitter <travis.reitter@collabora.co.uk>
26 public errordomain Tpf.PersonaError
32 * A persona subclass which represents a single instant messaging contact from
35 public class Tpf.Persona : Folks.Persona,
43 private HashTable<string, bool> _groups;
44 private bool _is_favourite;
45 private string _alias;
47 /* Whether we've finished being constructed; this is used to prevent
48 * unnecessary trips to the Telepathy service to tell it about properties
49 * being set which are actually just being set from data it's just given us.
51 private bool is_constructed = false;
56 public File avatar { get; set; }
61 public CapabilitiesFlags capabilities { get; private set; }
66 public Folks.PresenceType presence_type { get; private set; }
71 public string presence_message { get; private set; }
78 get { return this._alias; }
82 if (this._alias == value)
85 if (this.is_constructed)
86 ((Tpf.PersonaStore) this.store).change_alias (this, value);
94 public bool is_favourite
96 get { return this._is_favourite; }
100 if (this._is_favourite == value)
103 if (this.is_constructed)
104 ((Tpf.PersonaStore) this.store).change_is_favourite (this, value);
105 this._is_favourite = value;
112 public HashTable<string, bool> groups
114 get { return this._groups; }
118 value.foreach ((k, v) =>
120 var group = (string) k;
121 if (this._groups.lookup (group) == false)
122 this._change_group (group, true);
125 this._groups.foreach ((k, v) =>
127 var group = (string) k;
128 if (value.lookup (group) == false)
129 this._change_group (group, true);
137 public void change_group (string group, bool is_member)
139 if (_change_group (group, is_member))
141 ((Tpf.PersonaStore) this.store).change_group_membership (this, group,
144 this.group_changed (group, is_member);
148 private bool _change_group (string group, bool is_member)
150 bool changed = false;
154 if (this._groups.lookup (group) != true)
156 this._groups.insert (group, true);
161 changed = this._groups.remove (group);
167 * The Telepathy contact represented by this persona.
169 public Contact contact { get; construct; }
172 * Create a new persona.
174 * Create a new persona for the {@link PersonaStore} `store`, representing
175 * the Telepathy contact given by `contact`.
177 public Persona (Contact contact, PersonaStore store) throws Tpf.PersonaError
179 /* FIXME: There is the possibility of a crash in the error condition below
180 * due to bgo#604299, where the C self variable isn't initialised until we
181 * chain up to the Object constructor, below. */
182 var uid = contact.get_identifier ();
183 if (uid == null || uid == "")
184 throw new Tpf.PersonaError.INVALID_ARGUMENT ("contact has an " +
187 var account = account_for_connection (contact.get_connection ());
188 var account_id = ((Proxy) account).object_path;
189 /* this isn't meant to convey any real information, so no need to escape
190 * existing delimiters */
191 var iid = "telepathy:" + account_id + ":" + uid;
193 var alias = contact.get_alias ();
194 if (alias == null || alias.strip () == "")
197 Object (alias: alias,
203 this.is_constructed = true;
205 this._groups = new HashTable<string, bool> (str_hash, str_equal);
207 contact.notify["avatar-file"].connect ((s, p) =>
209 this.contact_notify_avatar ();
211 this.contact_notify_avatar ();
213 contact.notify["presence-message"].connect ((s, p) =>
215 this.contact_notify_presence_message ();
217 contact.notify["presence-type"].connect ((s, p) =>
219 this.contact_notify_presence_type ();
221 this.contact_notify_presence_message ();
222 this.contact_notify_presence_type ();
224 contact.notify["capabilities"].connect ((s, p) =>
226 this.contact_notify_capabilities ();
228 this.contact_notify_capabilities ();
230 ((Tpf.PersonaStore) this.store).group_members_changed.connect (
231 (s, group, added, removed) =>
233 if (added.find (this) != null)
234 this._change_group (group, true);
236 if (removed.find (this) != null)
237 this._change_group (group, false);
240 ((Tpf.PersonaStore) this.store).group_removed.connect (
244 warning ("group invalidated: %s", error.message);
246 this._change_group (group, false);
250 private static Account? account_for_connection (Connection conn)
252 var manager = AccountManager.dup ();
253 var accounts = manager.get_valid_accounts ();
255 Account account_found = null;
256 accounts.foreach ((l) =>
258 var account = (Account) l;
259 if (account.get_connection () == conn)
261 account_found = account;
266 return account_found;
269 private void contact_notify_presence_message ()
271 this.presence_message = this.contact.get_presence_message ();
274 private void contact_notify_presence_type ()
276 this.presence_type = folks_presence_type_from_tp (
277 this.contact.get_presence_type ());
280 private static PresenceType folks_presence_type_from_tp (
281 TelepathyGLib.ConnectionPresenceType type)
285 case TelepathyGLib.ConnectionPresenceType.AVAILABLE:
286 return PresenceType.AVAILABLE;
287 case TelepathyGLib.ConnectionPresenceType.AWAY:
288 return PresenceType.AWAY;
289 case TelepathyGLib.ConnectionPresenceType.BUSY:
290 return PresenceType.BUSY;
291 case TelepathyGLib.ConnectionPresenceType.ERROR:
292 return PresenceType.ERROR;
293 case TelepathyGLib.ConnectionPresenceType.EXTENDED_AWAY:
294 return PresenceType.EXTENDED_AWAY;
295 case TelepathyGLib.ConnectionPresenceType.HIDDEN:
296 return PresenceType.HIDDEN;
297 case TelepathyGLib.ConnectionPresenceType.OFFLINE:
298 return PresenceType.OFFLINE;
299 case TelepathyGLib.ConnectionPresenceType.UNKNOWN:
300 return PresenceType.UNKNOWN;
301 case TelepathyGLib.ConnectionPresenceType.UNSET:
302 return PresenceType.UNSET;
304 return PresenceType.UNKNOWN;
308 private void contact_notify_avatar ()
310 var file = this.contact.get_avatar_file ();
311 if (this.avatar != file)
315 private void contact_notify_capabilities ()
317 var caps = this.contact.get_capabilities ();
319 this.capabilities = folks_capabilities_flags_from_tp (caps);
322 /* Based off tp_caps_to_capabilities() in empathy-contact.c */
323 private static CapabilitiesFlags folks_capabilities_flags_from_tp (
324 TelepathyGLib.Capabilities caps)
326 CapabilitiesFlags capabilities = 0;
327 var classes = caps.get_channel_classes ();
329 classes.foreach ((m) =>
331 unowned ValueArray class_struct = (ValueArray) m;
333 unowned Value val = class_struct.get_nth (0);
334 unowned HashTable fixed_prop = (HashTable) val.get_boxed ();
336 TelepathyGLib.HandleType handle_type =
337 (TelepathyGLib.HandleType) TelepathyGLib.asv_get_uint32 (
338 fixed_prop, TelepathyGLib.PROP_CHANNEL_TARGET_HANDLE_TYPE,
340 if (handle_type != HandleType.CONTACT)
341 return; /* i.e. continue the loop */
343 unowned string chan_type = TelepathyGLib.asv_get_string (fixed_prop,
344 TelepathyGLib.PROP_CHANNEL_CHANNEL_TYPE);
346 if (chan_type == TelepathyGLib.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
348 capabilities |= CapabilitiesFlags.FILE_TRANSFER;
350 else if (chan_type == TelepathyGLib.IFACE_CHANNEL_TYPE_STREAM_TUBE)
352 var service = TelepathyGLib.asv_get_string (fixed_prop,
353 TelepathyGLib.PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE);
355 if (service == "rfb")
356 capabilities |= CapabilitiesFlags.STREAM_TUBE;
358 else if (chan_type == TelepathyGLib.IFACE_CHANNEL_TYPE_STREAMED_MEDIA)
360 val = class_struct.get_nth (1);
361 unowned string[] allowed_prop = (string[]) val.get_boxed ();
363 if (allowed_prop != null)
365 for (int i = 0; allowed_prop[i] != null; i++)
367 unowned string prop = allowed_prop[i];
370 TelepathyGLib.PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO)
371 capabilities |= CapabilitiesFlags.AUDIO;
373 TelepathyGLib.PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO)
374 capabilities |= CapabilitiesFlags.VIDEO;