~ubuntu-branches/ubuntu/quantal/folks/quantal

« back to all changes in this revision

Viewing changes to backends/eds/lib/edsf-persona.vala

  • Committer: Package Import Robot
  • Author(s): Robert Ancell
  • Date: 2012-09-12 09:48:10 UTC
  • mfrom: (1.6.2)
  • Revision ID: package-import@ubuntu.com-20120912094810-6zlx8889hcovxj7p
Tags: 0.7.4.1-0ubuntu1
* New upstream bugfix release
* debian/control:
  - Bump build-depends on libglib2.0-dev, valac-0.18, libvala-0.18-dev
* debian/libfolks-eds25.symbols:
* debian/libfolks25.symbols:
  - Updated

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 
29
29
/**
30
30
 * A persona subclass which represents a single EDS contact.
 
31
 *
 
32
 * Each {@link Edsf.Persona} instance represents a single EDS {@link E.Contact}.
 
33
 * When the contact is modified (either by this folks client, or a different
 
34
 * client), the {@link Edsf.Persona} remains the same, but is assigned a new
 
35
 * {@link E.Contact}. It then updates its properties from this new contact.
31
36
 */
32
37
public class Edsf.Persona : Folks.Persona,
33
38
    AntiLinkable,
75
80
    "email_1", "email_2", "email_3", "email_4"
76
81
  };
77
82
 
78
 
  [Deprecated]
79
83
  /**
80
84
   * vCard field names for miscellaneous URIs.
81
85
   *
82
86
   * @since 0.6.0
83
87
   */
 
88
  [Deprecated (since = "0.6.3",
 
89
      replacement = "Folks.UrlFieldDetails.PARAM_TYPE_BLOG")]
84
90
  public static const string[] url_properties = {
85
91
    "blog_url", "fburl", "homepage_url", "video_url"
86
92
  };
143
149
                                                  "local-ids",
144
150
                                                  "web-service-addresses" };
145
151
 
146
 
  private HashSet<PhoneFieldDetails> _phone_numbers;
147
 
  private Set<PhoneFieldDetails> _phone_numbers_ro;
148
 
  private HashSet<EmailFieldDetails> _email_addresses;
149
 
  private Set<EmailFieldDetails> _email_addresses_ro;
150
 
  private HashSet<NoteFieldDetails> _notes;
151
 
  private Set<NoteFieldDetails> _notes_ro;
152
152
  private static HashTable<string, E.ContactField>? _im_eds_map = null;
153
153
 
154
 
  private HashSet<PostalAddressFieldDetails> _postal_addresses;
155
 
  private Set<PostalAddressFieldDetails> _postal_addresses_ro;
156
 
 
157
 
  private HashSet<string> _local_ids;
158
 
  private Set<string> _local_ids_ro;
159
 
 
160
 
  private HashMultiMap<string, WebServiceFieldDetails> _web_service_addresses;
161
 
 
162
 
  private bool _is_favourite;
163
 
 
164
154
  private E.Contact _contact; /* should be set on construct */
165
155
 
166
156
  /**
172
162
      construct { this._contact = value; }
173
163
    }
174
164
 
 
165
  /* NOTE: Other properties support lazy initialisation, but
 
166
   * web-service-addresses doesn't as it's a linkable property, so always has to
 
167
   * be loaded anyway. */
 
168
  private HashMultiMap<string, WebServiceFieldDetails> _web_service_addresses;
 
169
 
175
170
  /**
176
171
   * {@inheritDoc}
177
172
   */
195
190
          web_service_addresses);
196
191
    }
197
192
 
 
193
  /* NOTE: Other properties support lazy initialisation, but local-ids doesn't
 
194
   * as it's a linkable property, so always has to be loaded anyway. */
 
195
  private HashSet<string> _local_ids = new HashSet<string> ();
 
196
  private Set<string> _local_ids_ro;
 
197
 
198
198
  /**
199
199
   * IDs used to link {@link Edsf.Persona}s.
200
200
   */
223
223
      yield ((Edsf.PersonaStore) this.store)._set_local_ids (this, local_ids);
224
224
    }
225
225
 
 
226
  private HashSet<PostalAddressFieldDetails>? _postal_addresses = null;
 
227
  private Set<PostalAddressFieldDetails>? _postal_addresses_ro = null;
 
228
 
226
229
  /**
227
230
   * The postal addresses of the contact.
228
231
   *
233
236
  [CCode (notify = false)]
234
237
  public Set<PostalAddressFieldDetails> postal_addresses
235
238
    {
236
 
      get { return this._postal_addresses_ro; }
 
239
      get
 
240
        {
 
241
          this._update_addresses (true, false);
 
242
          return this._postal_addresses_ro;
 
243
        }
237
244
      set { this.change_postal_addresses.begin (value); }
238
245
    }
239
246
 
249
256
          postal_addresses);
250
257
    }
251
258
 
 
259
  private HashSet<PhoneFieldDetails>? _phone_numbers = null;
 
260
  private Set<PhoneFieldDetails>? _phone_numbers_ro = null;
 
261
 
252
262
  /**
253
263
   * {@inheritDoc}
254
264
   *
257
267
  [CCode (notify = false)]
258
268
  public Set<PhoneFieldDetails> phone_numbers
259
269
    {
260
 
      get { return this._phone_numbers_ro; }
 
270
      get
 
271
        {
 
272
          this._update_phones (true, false);
 
273
          return this._phone_numbers_ro;
 
274
        }
261
275
      set { this.change_phone_numbers.begin (value); }
262
276
    }
263
277
 
272
286
      yield ((Edsf.PersonaStore) this.store)._set_phones (this, phone_numbers);
273
287
    }
274
288
 
 
289
  private HashSet<EmailFieldDetails>? _email_addresses = null;
 
290
  private Set<EmailFieldDetails>? _email_addresses_ro = null;
 
291
 
275
292
  /**
276
293
   * {@inheritDoc}
277
294
   *
280
297
  [CCode (notify = false)]
281
298
  public Set<EmailFieldDetails> email_addresses
282
299
    {
283
 
      get { return this._email_addresses_ro; }
 
300
      get
 
301
        {
 
302
          this._update_emails (true, false);
 
303
          return this._email_addresses_ro;
 
304
        }
284
305
      set { this.change_email_addresses.begin (value); }
285
306
    }
286
307
 
296
317
          email_addresses);
297
318
    }
298
319
 
 
320
  private HashSet<NoteFieldDetails>? _notes = null;
 
321
  private Set<NoteFieldDetails>? _notes_ro = null;
 
322
 
299
323
  /**
300
324
   * {@inheritDoc}
301
325
   *
304
328
  [CCode (notify = false)]
305
329
  public Set<NoteFieldDetails> notes
306
330
    {
307
 
      get { return this._notes_ro; }
 
331
      get
 
332
        {
 
333
          this._update_notes (true, false);
 
334
          return this._notes_ro;
 
335
        }
308
336
      set { this.change_notes.begin (value); }
309
337
    }
310
338
 
326
354
   */
327
355
  public override string[] linkable_properties
328
356
    {
329
 
      get { return this._linkable_properties; }
 
357
      get { return Persona._linkable_properties; }
330
358
    }
331
359
 
332
360
  /**
465
493
      yield ((Edsf.PersonaStore) this.store)._set_gender (this, gender);
466
494
    }
467
495
 
468
 
  private HashSet<UrlFieldDetails> _urls;
469
 
  private Set<UrlFieldDetails> _urls_ro;
 
496
  private HashSet<UrlFieldDetails>? _urls = null;
 
497
  private Set<UrlFieldDetails>? _urls_ro = null;
470
498
  /**
471
499
   * {@inheritDoc}
472
500
   *
475
503
  [CCode (notify = false)]
476
504
  public Set<UrlFieldDetails> urls
477
505
    {
478
 
      get { return this._urls_ro; }
 
506
      get
 
507
        {
 
508
          this._update_urls (true, false);
 
509
          return this._urls_ro;
 
510
        }
479
511
      set { this.change_urls.begin (value); }
480
512
    }
481
513
 
489
521
      yield ((Edsf.PersonaStore) this.store)._set_urls (this, urls);
490
522
    }
491
523
 
 
524
  /* NOTE: Other properties support lazy initialisation, but im-addresses
 
525
   * doesn't as it's a linkable property, so always has to be loaded anyway. */
492
526
  private HashMultiMap<string, ImFieldDetails> _im_addresses =
493
527
      new HashMultiMap<string, ImFieldDetails> (null, null,
494
528
          (GLib.HashFunc) ImFieldDetails.hash,
517
551
      yield ((Edsf.PersonaStore) this.store)._set_im_fds (this, im_addresses);
518
552
    }
519
553
 
520
 
  private HashSet<string> _groups;
521
 
  private Set<string> _groups_ro;
 
554
  private HashSet<string>? _groups = null;
 
555
  private Set<string>? _groups_ro = null;
522
556
 
523
557
  /**
524
558
   * {@inheritDoc}
528
562
  [CCode (notify = false)]
529
563
  public Set<string> groups
530
564
    {
531
 
      get { return this._groups_ro; }
 
565
      get
 
566
        {
 
567
          this._update_groups (true, false);
 
568
          return this._groups_ro;
 
569
        }
532
570
      set { this.change_groups.begin (value); }
533
571
    }
534
572
 
540
578
  public async void change_group (string group, bool is_member)
541
579
      throws GLib.Error
542
580
    {
 
581
      /* NOTE: This method specifically accesses this.groups rather than
 
582
       * this._groups, so that lazy loading is guaranteed to happen if
 
583
       * necessary. */
543
584
      /* Nothing to do? */
544
 
      if ((is_member == true && this._groups.contains (group) == true) ||
545
 
          (is_member == false && this._groups.contains (group) == false))
 
585
      if ((is_member == true && this.groups.contains (group) == true) ||
 
586
          (is_member == false && this.groups.contains (group) == false))
546
587
        {
547
588
          return;
548
589
        }
549
590
 
550
591
      /* Replace the current set of groups with a modified one. */
551
592
      var new_groups = new HashSet<string> ();
552
 
      foreach (var category_name in this._groups)
 
593
      foreach (var category_name in this.groups)
553
594
        {
554
595
          new_groups.add (category_name);
555
596
        }
619
660
          bday);
620
661
    }
621
662
 
622
 
  private HashSet<RoleFieldDetails> _roles;
623
 
  private Set<RoleFieldDetails> _roles_ro;
 
663
  private HashSet<RoleFieldDetails>? _roles = null;
 
664
  private Set<RoleFieldDetails>? _roles_ro = null;
624
665
 
625
666
  /**
626
667
   * {@inheritDoc}
630
671
  [CCode (notify = false)]
631
672
  public Set<RoleFieldDetails> roles
632
673
    {
633
 
      get { return this._roles_ro; }
 
674
      get
 
675
        {
 
676
          this._update_roles (true, false);
 
677
          return this._roles_ro;
 
678
        }
634
679
      set { this.change_roles.begin (value); }
635
680
    }
636
681
 
645
690
      yield ((Edsf.PersonaStore) this.store)._set_roles (this, roles);
646
691
    }
647
692
 
 
693
  private bool _is_favourite = false;
 
694
 
648
695
  /**
649
696
   * Whether this contact is a user-defined favourite.
650
697
   *
653
700
  [CCode (notify = false)]
654
701
  public bool is_favourite
655
702
      {
656
 
        get { return this._is_favourite; }
 
703
        get
 
704
          {
 
705
            this._update_groups (true, false); /* also checks for favourites */
 
706
            return this._is_favourite;
 
707
          }
657
708
        set { this.change_is_favourite.begin (value); }
658
709
      }
659
710
 
710
761
  [CCode (notify = false)]
711
762
  public bool in_google_personal_group
712
763
    {
713
 
      get { return this._in_google_personal_group; }
 
764
      get
 
765
        {
 
766
          this._update_groups (true); /* also checks for the personal group */
 
767
          return this._in_google_personal_group;
 
768
        }
714
769
    }
715
770
 
716
771
  /**
748
803
  /**
749
804
   * Create a new persona.
750
805
   *
751
 
   * Create a new persona for the {@link PersonaStore} `store`, representing
752
 
   * the EDS contact given by `contact`.
 
806
   * Create a new persona for the {@link PersonaStore} ``store``, representing
 
807
   * the EDS contact given by ``contact``.
753
808
   *
754
809
   * @param store the store which will contain the persona
755
810
   * @param contact the EDS contact being represented by the persona
762
817
          Edsf.Persona._get_property_from_contact<string> (contact, "id");
763
818
      var contact_id = (!) (_contact_id ?? "");
764
819
 
765
 
      var uid = this.build_uid (BACKEND_NAME, store.id, contact_id);
 
820
      var uid = Folks.Persona.build_uid (BACKEND_NAME, store.id, contact_id);
766
821
      var iid = Edsf.Persona.build_iid (store.id, contact_id);
767
822
      var is_user = BookClient.is_self (contact);
768
823
      var _full_name =
784
839
      debug ("Creating new Edsf.Persona with IID '%s'", this.iid);
785
840
 
786
841
      this._gender = Gender.UNSPECIFIED;
787
 
      this._phone_numbers = new HashSet<PhoneFieldDetails> (
788
 
          (GLib.HashFunc) PhoneFieldDetails.hash,
789
 
          (GLib.EqualFunc) PhoneFieldDetails.equal);
790
 
      this._phone_numbers_ro = this._phone_numbers.read_only_view;
791
 
      this._email_addresses = new HashSet<EmailFieldDetails> (
792
 
          (GLib.HashFunc) EmailFieldDetails.hash,
793
 
          (GLib.EqualFunc) EmailFieldDetails.equal);
794
 
      this._email_addresses_ro = this._email_addresses.read_only_view;
795
 
      this._notes = new HashSet<NoteFieldDetails> (
796
 
          (GLib.HashFunc) NoteFieldDetails.hash,
797
 
          (GLib.EqualFunc) NoteFieldDetails.equal);
798
 
      this._notes_ro = this._notes.read_only_view;
799
 
      this._urls = new HashSet<UrlFieldDetails> (
800
 
          (GLib.HashFunc) UrlFieldDetails.hash,
801
 
          (GLib.EqualFunc) UrlFieldDetails.equal);
802
 
      this._urls_ro = this._urls.read_only_view;
803
 
      this._postal_addresses = new HashSet<PostalAddressFieldDetails> (
804
 
          (GLib.HashFunc) PostalAddressFieldDetails.hash,
805
 
          (GLib.EqualFunc) PostalAddressFieldDetails.equal);
806
 
      this._postal_addresses_ro = this._postal_addresses.read_only_view;
807
 
      this._local_ids = new HashSet<string> ();
808
842
      this._local_ids_ro = this._local_ids.read_only_view;
809
843
      this._web_service_addresses =
810
844
        new HashMultiMap<string, WebServiceFieldDetails> (
811
845
            null, null,
812
846
            (GLib.HashFunc) WebServiceFieldDetails.hash,
813
847
            (GLib.EqualFunc) WebServiceFieldDetails.equal);
814
 
      this._groups = new HashSet<string> ();
815
 
      this._groups_ro = this._groups.read_only_view;
816
 
      this._roles = new HashSet<RoleFieldDetails> (
817
 
          (GLib.HashFunc) RoleFieldDetails.hash,
818
 
          (GLib.EqualFunc) RoleFieldDetails.equal);
819
 
      this._roles_ro = this._roles.read_only_view;
820
848
      this._anti_links = new HashSet<string> ();
821
849
      this._anti_links_ro = this._anti_links.read_only_view;
822
850
 
889
917
 
890
918
      this._update_names ();
891
919
      this._update_avatar ();
892
 
      this._update_urls ();
893
 
      this._update_phones ();
894
 
      this._update_addresses ();
895
 
      this._update_emails ();
 
920
      this._update_urls (false);
 
921
      this._update_phones (false);
 
922
      this._update_addresses (false);
 
923
      this._update_emails (false);
896
924
 
897
925
      /* Note: because we assume certain e-mail addresses
898
926
       * (@gmail, @msn, etc) to also be IM IDs we /must/
900
928
       */
901
929
      this._update_im_addresses ();
902
930
 
903
 
      this._update_groups ();
904
 
      this._update_notes ();
 
931
      this._update_groups (false);
 
932
      this._update_notes (false);
905
933
      this._update_local_ids ();
906
934
      this._update_web_services_addresses ();
907
935
      this._update_gender ();
908
936
      this._update_birthday ();
909
 
      this._update_roles ();
 
937
      this._update_roles (false);
910
938
      this._update_favourite ();
911
939
      this._update_anti_links ();
912
940
 
979
1007
           * by the BirthdayDetails interface.
980
1008
           * We cache the timezone since creating it requires mmapping
981
1009
           * /etc/localtime, which means lots of syscalls. */
982
 
          var d = new DateTime (this._local_time_zone,
 
1010
          var d = new DateTime (Persona._local_time_zone,
983
1011
              (int) bday.year, (int) bday.month, (int) bday.day, 0, 0, 0.0);
984
1012
          if (this._birthday == null ||
985
1013
              (this._birthday != null &&
999
1027
        }
1000
1028
    }
1001
1029
 
1002
 
  private void _update_roles ()
 
1030
  private void _update_roles (bool create_if_not_exist, bool emit_notification = true)
1003
1031
    {
 
1032
      /* See the comments in Folks.Individual about the lazy instantiation
 
1033
       * strategy for roles. */
 
1034
      if (this._roles == null && create_if_not_exist == false)
 
1035
        {
 
1036
          if (emit_notification)
 
1037
            {
 
1038
              this.notify_property ("roles");
 
1039
            }
 
1040
          return;
 
1041
        }
 
1042
      else if (this._roles == null)
 
1043
        {
 
1044
          this._roles = new HashSet<RoleFieldDetails> (
 
1045
              (GLib.HashFunc) RoleFieldDetails.hash,
 
1046
              (GLib.EqualFunc) RoleFieldDetails.equal);
 
1047
          this._roles_ro = this._roles.read_only_view;
 
1048
        }
 
1049
 
1004
1050
      var new_roles = new HashSet<RoleFieldDetails> (
1005
1051
          (GLib.HashFunc) RoleFieldDetails.hash,
1006
1052
          (GLib.EqualFunc) RoleFieldDetails.equal);
1067
1113
        {
1068
1114
          this._roles = new_roles;
1069
1115
          this._roles_ro = new_roles.read_only_view;
1070
 
          this.notify_property ("roles");
 
1116
          if (emit_notification)
 
1117
            {
 
1118
              this.notify_property ("roles");
 
1119
            }
1071
1120
        }
1072
1121
    }
1073
1122
 
1153
1202
        }
1154
1203
    }
1155
1204
 
1156
 
  private void _update_emails ()
 
1205
  private void _update_emails (bool create_if_not_exist, bool emit_notification = true)
1157
1206
    {
 
1207
      /* See the comments in Folks.Individual about the lazy instantiation
 
1208
       * strategy for e-mail addresses. */
 
1209
      if (this._email_addresses == null && create_if_not_exist == false)
 
1210
        {
 
1211
          if (emit_notification)
 
1212
            {
 
1213
              this.notify_property ("email-addresses");
 
1214
            }
 
1215
          return;
 
1216
        }
 
1217
      else if (this._email_addresses == null)
 
1218
        {
 
1219
          this._email_addresses = new HashSet<EmailFieldDetails> (
 
1220
              (GLib.HashFunc) EmailFieldDetails.hash,
 
1221
              (GLib.EqualFunc) EmailFieldDetails.equal);
 
1222
          this._email_addresses_ro = this._email_addresses.read_only_view;
 
1223
        }
 
1224
 
1158
1225
      var new_email_addresses = new HashSet<EmailFieldDetails> (
1159
1226
          (GLib.HashFunc) EmailFieldDetails.hash,
1160
1227
          (GLib.EqualFunc) EmailFieldDetails.equal);
1176
1243
      if (!Folks.Internal.equal_sets<EmailFieldDetails> (new_email_addresses,
1177
1244
              this._email_addresses))
1178
1245
        {
1179
 
         this._email_addresses = new_email_addresses;
1180
 
         this._email_addresses_ro = new_email_addresses.read_only_view;
1181
 
         this.notify_property ("email-addresses");
 
1246
          this._email_addresses = new_email_addresses;
 
1247
          this._email_addresses_ro = new_email_addresses.read_only_view;
 
1248
          if (emit_notification)
 
1249
            {
 
1250
              this.notify_property ("email-addresses");
 
1251
            }
1182
1252
       }
1183
1253
    }
1184
1254
 
1185
 
  private void _update_notes ()
 
1255
  private void _update_notes (bool create_if_not_exist, bool emit_notification = true)
1186
1256
    {
 
1257
      /* See the comments in Folks.Individual about the lazy instantiation
 
1258
       * strategy for notes. */
 
1259
      if (this._notes == null && create_if_not_exist == false)
 
1260
        {
 
1261
          if (emit_notification)
 
1262
            {
 
1263
              this.notify_property ("notes");
 
1264
            }
 
1265
          return;
 
1266
        }
 
1267
      else if (this._notes == null)
 
1268
        {
 
1269
          this._notes = new HashSet<NoteFieldDetails> (
 
1270
              (GLib.HashFunc) NoteFieldDetails.hash,
 
1271
              (GLib.EqualFunc) NoteFieldDetails.equal);
 
1272
          this._notes_ro = this._notes.read_only_view;
 
1273
        }
 
1274
 
1187
1275
      var new_notes = new HashSet<NoteFieldDetails> (
1188
1276
          (GLib.HashFunc) NoteFieldDetails.hash,
1189
1277
          (GLib.EqualFunc) NoteFieldDetails.equal);
1199
1287
        {
1200
1288
          this._notes = new_notes;
1201
1289
          this._notes_ro = this._notes.read_only_view;
1202
 
          this.notify_property ("notes");
 
1290
          if (emit_notification)
 
1291
            {
 
1292
              this.notify_property ("notes");
 
1293
            }
1203
1294
        }
1204
1295
    }
1205
1296
 
1344
1435
        }
1345
1436
    }
1346
1437
 
1347
 
  private void _update_urls ()
 
1438
  private void _update_urls (bool create_if_not_exist, bool emit_notification = true)
1348
1439
    {
1349
 
      var new_urls = new HashSet<UrlFieldDetails> ();
 
1440
      /* See the comments in Folks.Individual about the lazy instantiation
 
1441
       * strategy for URIs. */
 
1442
      if (this._urls == null && create_if_not_exist == false)
 
1443
        {
 
1444
          if (emit_notification)
 
1445
            {
 
1446
              this.notify_property ("urls");
 
1447
            }
 
1448
          return;
 
1449
        }
 
1450
      else if (this._urls == null)
 
1451
        {
 
1452
          this._urls = new HashSet<UrlFieldDetails> (
 
1453
              (GLib.HashFunc) UrlFieldDetails.hash,
 
1454
              (GLib.EqualFunc) UrlFieldDetails.equal);
 
1455
          this._urls_ro = this._urls.read_only_view;
 
1456
        }
 
1457
 
 
1458
      var new_urls = new HashSet<UrlFieldDetails> (
 
1459
          (GLib.HashFunc) UrlFieldDetails.hash,
 
1460
          (GLib.EqualFunc) UrlFieldDetails.equal);
1350
1461
 
1351
1462
      /* First we get the standard Evo urls.. */
1352
 
      foreach (var mapping in this._url_properties)
 
1463
      foreach (var mapping in Persona._url_properties)
1353
1464
        {
1354
1465
          var url_property = mapping.vcard_field_name;
1355
1466
          var folks_type = mapping.folks_type;
1358
1469
          if (u != null && u != "")
1359
1470
            {
1360
1471
              var fd_u = new UrlFieldDetails ((!) u);
1361
 
              fd_u.set_parameter (fd_u.PARAM_TYPE, folks_type);
 
1472
              fd_u.set_parameter (AbstractFieldDetails.PARAM_TYPE, folks_type);
1362
1473
              new_urls.add (fd_u);
1363
1474
            }
1364
1475
        }
1385
1496
        {
1386
1497
          this._urls = new_urls;
1387
1498
          this._urls_ro = new_urls.read_only_view;
1388
 
          this.notify_property ("urls");
 
1499
          if (emit_notification)
 
1500
            {
 
1501
              this.notify_property ("urls");
 
1502
            }
1389
1503
        }
1390
1504
    }
1391
1505
 
1392
1506
  private void _update_im_addresses ()
1393
1507
    {
1394
 
      var im_eds_map = this._get_im_eds_map ();
 
1508
      var im_eds_map = Persona._get_im_eds_map ();
1395
1509
      var new_im_addresses = new HashMultiMap<string, ImFieldDetails> (null,
1396
1510
          null, (GLib.HashFunc) ImFieldDetails.hash,
1397
1511
          (GLib.EqualFunc) ImFieldDetails.equal);
1438
1552
       * who don't actually use GMail or MSN addresses for IM).
1439
1553
       *
1440
1554
       * See bgo#657142
 
1555
       *
 
1556
       * NOTE: The public property name (this.email_addresses, as opposed to
 
1557
       * this._email_addresses) is used here to ensure the values are
 
1558
       * lazy-loaded correctly.
1441
1559
       */
1442
1560
      foreach (var email in this.email_addresses)
1443
1561
        {
1489
1607
        }
1490
1608
    }
1491
1609
 
1492
 
  private void _update_groups ()
 
1610
  private void _update_groups (bool create_if_not_exist, bool emit_notification = true)
1493
1611
    {
 
1612
      /* See the comments in Folks.Individual about the lazy instantiation
 
1613
       * strategy for groups. */
 
1614
      if (this._groups == null && create_if_not_exist == false)
 
1615
        {
 
1616
          if (emit_notification)
 
1617
            {
 
1618
              this.notify_property ("groups");
 
1619
            }
 
1620
          return;
 
1621
        }
 
1622
      else if (this._groups == null)
 
1623
        {
 
1624
          this._groups = new HashSet<string> ();
 
1625
          this._groups_ro = this._groups.read_only_view;
 
1626
        }
 
1627
 
1494
1628
      var category_names =
1495
1629
          this._contact.get<GLib.List<string>> (E.ContactField.CATEGORY_LIST);
1496
1630
      var new_categories = new HashSet<string> ();
1583
1717
      /* Notify if anything's changed. */
1584
1718
      this.freeze_notify ();
1585
1719
 
1586
 
      if (added_categories.size != 0 || removed_categories.size != 0)
 
1720
      if ((added_categories.size != 0 || removed_categories.size != 0) &&
 
1721
         emit_notification)
1587
1722
        {
1588
1723
          this.notify_property ("groups");
1589
1724
        }
1590
 
      if (this._is_favourite != old_is_favourite)
 
1725
      if (this._is_favourite != old_is_favourite && emit_notification)
1591
1726
        {
1592
1727
          this.notify_property ("is-favourite");
1593
1728
        }
1594
1729
      if (in_google_personal_group != this._in_google_personal_group)
1595
1730
        {
1596
1731
          this._in_google_personal_group = in_google_personal_group;
1597
 
          this.notify_property ("in-google-personal-group");
 
1732
          if (emit_notification)
 
1733
            {
 
1734
              this.notify_property ("in-google-personal-group");
 
1735
            }
1598
1736
        }
1599
1737
 
1600
1738
      this.thaw_notify ();
1632
1770
      return retval;
1633
1771
    }
1634
1772
 
1635
 
  private void _update_phones ()
 
1773
  private void _update_phones (bool create_if_not_exist, bool emit_notification = true)
1636
1774
    {
 
1775
      /* See the comments in Folks.Individual about the lazy instantiation
 
1776
       * strategy for phone numbers. */
 
1777
      if (this._phone_numbers == null && create_if_not_exist == false)
 
1778
        {
 
1779
          this.notify_property ("phone-numbers");
 
1780
          return;
 
1781
        }
 
1782
      else if (this._phone_numbers == null)
 
1783
        {
 
1784
          this._phone_numbers = new HashSet<PhoneFieldDetails> (
 
1785
              (GLib.HashFunc) PhoneFieldDetails.hash,
 
1786
              (GLib.EqualFunc) PhoneFieldDetails.equal);
 
1787
          this._phone_numbers_ro = this._phone_numbers.read_only_view;
 
1788
        }
 
1789
 
1637
1790
      var new_phone_numbers = new HashSet<PhoneFieldDetails> (
1638
1791
          (GLib.HashFunc) PhoneFieldDetails.hash,
1639
1792
          (GLib.EqualFunc) PhoneFieldDetails.equal);
1657
1810
        {
1658
1811
          this._phone_numbers = new_phone_numbers;
1659
1812
          this._phone_numbers_ro = new_phone_numbers.read_only_view;
1660
 
          this.notify_property ("phone-numbers");
 
1813
          if (emit_notification)
 
1814
            {
 
1815
              this.notify_property ("phone-numbers");
 
1816
            }
1661
1817
        }
1662
1818
   }
1663
1819
 
1721
1877
   *       are the same and if so instantiate only one PostalAddress
1722
1878
   *       (with the given types).
1723
1879
   */
1724
 
  private void _update_addresses ()
 
1880
  private void _update_addresses (bool create_if_not_exist, bool emit_notification = true)
1725
1881
    {
 
1882
      /* See the comments in Folks.Individual about the lazy instantiation
 
1883
       * strategy for addresses. */
 
1884
      if (this._postal_addresses == null && create_if_not_exist == false)
 
1885
        {
 
1886
          if (emit_notification)
 
1887
            {
 
1888
              this.notify_property ("postal-addresses");
 
1889
            }
 
1890
          return;
 
1891
        }
 
1892
      else if (this._postal_addresses == null)
 
1893
        {
 
1894
          this._postal_addresses = new HashSet<PostalAddressFieldDetails> (
 
1895
              (GLib.HashFunc) PostalAddressFieldDetails.hash,
 
1896
              (GLib.EqualFunc) PostalAddressFieldDetails.equal);
 
1897
          this._postal_addresses_ro = this._postal_addresses.read_only_view;
 
1898
        }
 
1899
 
1726
1900
      var new_postal_addresses = new HashSet<PostalAddressFieldDetails> (
1727
 
          (GLib.HashFunc) PhoneFieldDetails.hash,
1728
 
          (GLib.EqualFunc) PhoneFieldDetails.equal);
 
1901
          (GLib.HashFunc) PostalAddressFieldDetails.hash,
 
1902
          (GLib.EqualFunc) PostalAddressFieldDetails.equal);
1729
1903
 
1730
1904
      var attrs = this.contact.get_attributes (E.ContactField.ADDRESS);
1731
1905
      foreach (unowned E.VCardAttribute attr in attrs)
1747
1921
        {
1748
1922
          this._postal_addresses = new_postal_addresses;
1749
1923
          this._postal_addresses_ro = new_postal_addresses.read_only_view;
1750
 
          this.notify_property ("phone-numbers");
 
1924
          if (emit_notification)
 
1925
            {
 
1926
              this.notify_property ("postal-addresses");
 
1927
            }
1751
1928
        }
1752
 
 
1753
 
      this.notify_property ("postal-addresses");
1754
1929
    }
1755
1930
 
1756
1931
  private void _update_local_ids ()