113
111
/* The hardcoded prio if set, otherwise 0 for the
114
main/combined group, or -2 for the separated other group */
112
main/combined group, or -1 for the separated other group */
115
113
private int get_sort_prio (ContactData *data) {
116
114
if (data->sort_prio != 0)
117
115
return data->sort_prio;
119
117
if (is_other (data))
124
public string get_header_text (TreeIter iter) {
126
list_store.get (iter, 1, out data);
127
if (data == suggestions_header_data) {
128
/* Translators: This is the header for the list of suggested contacts to
129
link to the current contact */
130
return ngettext ("Suggestion", "Suggestions", custom_visible_count);
132
if (data == other_header_data) {
133
/* Translators: This is the header for the list of suggested contacts to
134
link to the current contact */
135
return _("Other Contacts");
140
122
public void set_show_subset (Subset subset) {
141
123
show_subset = subset;
143
bool new_visible = show_subset == Subset.ALL_SEPARATED;
144
if (new_visible && !other_header_data.visible) {
145
other_header_data.visible = true;
146
list_store.append (out other_header_data.iter);
147
list_store.set (other_header_data.iter, 1, other_header_data);
149
if (!new_visible && other_header_data.visible) {
150
other_header_data.visible = false;
151
list_store.remove (other_header_data.iter);
124
update_all_filtered ();
157
129
public void set_custom_sort_prio (Contact c, int prio) {
158
130
/* We use negative prios internally */
159
131
assert (prio >= 0);
161
var data = lookup_data (c);
133
var data = contacts.get (c);
163
134
if (data == null)
166
// We insert a priority between 0 and 1 for the padding
169
136
data.sort_prio = prio;
170
contact_changed_cb (contacts_store, c);
174
if (custom_visible_count++ == 0)
175
add_custom_headers ();
177
if (custom_visible_count-- == 1)
178
remove_custom_headers ();
183
private bool apply_filter (Contact contact) {
184
if (contact.is_hidden)
187
if (contact in hidden_contacts)
190
if ((show_subset == Subset.MAIN &&
192
(show_subset == Subset.OTHER &&
196
if (filter_values == null || filter_values.length == 0)
199
return contact.contains_strings (filter_values);
202
public bool is_first (TreeIter iter) {
204
list_store.get (iter, 1, out data);
206
return data->is_first;
210
private ContactData? get_previous (ContactData data) {
211
ContactData *previous = null;
212
TreeIter iter = data.iter;
213
if (list_store.iter_previous (ref iter))
214
list_store.get (iter, 1, out previous);
218
private ContactData? get_next (ContactData data) {
219
ContactData *next = null;
220
TreeIter iter = data.iter;
221
if (list_store.iter_next (ref iter))
222
list_store.get (iter, 1, out next);
226
private void row_changed_no_resort (ContactData data) {
227
var path = list_store.get_path (data.iter);
228
list_store.row_changed (path, data.iter);
231
private void row_changed_resort (ContactData data) {
232
list_store.set (data.iter, 0, data.contact);
235
private bool update_is_first (ContactData data, ContactData? previous) {
236
bool old_is_first = data.is_first;
238
bool is_custom = data.sort_prio != 0;
239
bool previous_is_custom = previous != null && (previous.sort_prio != 0) ;
242
data.is_first = false;
243
} else if (previous != null && !previous_is_custom) {
244
unichar previous_initial = previous.contact.initial_letter;
245
unichar initial = data.contact.initial_letter;
246
data.is_first = previous_initial != initial;
248
data.is_first = true;
251
if (old_is_first != data.is_first) {
252
row_changed_no_resort (data);
259
private void add_custom_headers () {
260
suggestions_header_data.visible = true;
261
list_store.append (out suggestions_header_data.iter);
262
list_store.set (suggestions_header_data.iter, 1, suggestions_header_data);
263
padding_data.visible = true;
264
list_store.append (out padding_data.iter);
265
list_store.set (padding_data.iter, 1, padding_data);
268
private void remove_custom_headers () {
269
suggestions_header_data.visible = false;
270
list_store.remove (suggestions_header_data.iter);
271
padding_data.visible = false;
272
list_store.remove (padding_data.iter);
275
private void add_to_model (ContactData data) {
276
list_store.append (out data.iter);
277
list_store.set (data.iter, 0, data.contact, 1, data);
279
if (data.sort_prio > 0) {
280
if (custom_visible_count++ == 0)
281
add_custom_headers ();
284
if (update_is_first (data, get_previous (data)) && data.is_first) {
285
/* The newly added row is first, the next one might not be anymore */
286
var next = get_next (data);
288
update_is_first (next, data);
292
private void remove_from_model (ContactData data) {
293
if (data.sort_prio > 0) {
294
if (custom_visible_count-- == 1)
295
remove_custom_headers ();
298
ContactData? next = null;
300
next = get_next (data);
302
list_store.remove (data.iter);
303
data.is_first = false;
306
update_is_first (next, get_previous (next));
309
private void update_visible (ContactData data) {
310
bool was_visible = data.visible;
311
data.visible = apply_filter (data.contact);
313
if (was_visible && !data.visible)
314
remove_from_model (data);
316
if (!was_visible && data.visible)
320
private void refilter () {
321
foreach (var c in contacts_store.get_contacts ()) {
322
update_visible (lookup_data (c));
137
child_changed (data.grid);
326
140
public void hide_contact (Contact contact) {
327
141
hidden_contacts.add (contact);
142
update_all_filtered ();
331
146
public void set_filter_values (string []? values) {
332
147
filter_values = values;
148
update_all_filtered ();
152
private bool calculate_filtered (Contact c) {
156
if (c in hidden_contacts)
159
if ((show_subset == Subset.MAIN &&
161
(show_subset == Subset.OTHER &&
165
if (filter_values == null || filter_values.length == 0)
168
return c.contains_strings (filter_values);
171
private void update_data (ContactData data) {
172
var c = data.contact;
173
data.display_name = c.display_name;
174
data.initial_letter = c.initial_letter;
175
data.filtered = calculate_filtered (c);
177
data.label.set_markup (Markup.printf_escaped ("<span font='16px'>%s</span>", data.display_name));
178
data.image_frame.set_image (c.individual, c);
181
private void update_all_filtered () {
182
foreach (var data in contacts.values) {
183
data.filtered = calculate_filtered (data.contact);
336
187
private void contact_changed_cb (Store store, Contact c) {
337
ContactData data = lookup_data (c);
339
bool was_visible = data.visible;
341
ContactData? next = null;
343
next = get_next (data);
345
update_visible (data);
347
if (was_visible && data.visible) {
348
/* We just moved position in the list while visible */
350
row_changed_resort (data);
352
/* Update the is_first on the previous next row */
354
update_is_first (next, get_previous (next));
356
/* Update the is_first on the new next row */
357
next = get_next (data);
359
update_is_first (next, data);
363
private ContactData lookup_data (Contact c) {
364
return c.lookup<ContactData> (this);
188
var data = contacts.get (c);
190
child_changed (data.grid);
367
193
private void contact_added_cb (Store store, Contact c) {
368
ContactData data = new ContactData();
194
var data = new ContactData();
369
195
data.contact = c;
370
data.visible = false;
372
c.set_lookup (this, data);
374
update_visible (data);
196
data.grid = new Grid ();
197
data.grid.margin = 12;
198
data.grid.set_column_spacing (10);
199
data.image_frame = new ContactFrame (Contact.SMALL_AVATAR_SIZE);
200
data.label = new Label ("");
201
data.label.set_ellipsize (Pango.EllipsizeMode.END);
202
data.label.set_valign (Align.START);
203
data.label.set_halign (Align.START);
205
data.grid.attach (data.image_frame, 0, 0, 1, 2);
206
data.grid.attach (data.label, 1, 0, 1, 1);
208
if (text_display == TextDisplay.PRESENCE) {
209
var merged_presence = c.create_merged_presence_widget ();
210
merged_presence.set_halign (Align.START);
211
merged_presence.set_valign (Align.END);
212
merged_presence.set_vexpand (false);
213
merged_presence.set_margin_bottom (4);
215
data.grid.attach (merged_presence, 1, 1, 1, 1);
218
if (text_display == TextDisplay.STORES) {
219
var stores = new Label ("");
220
stores.set_markup (Markup.printf_escaped ("<span font='12px'>%s</span>",
221
c.format_persona_stores ()));
223
stores.set_ellipsize (Pango.EllipsizeMode.END);
224
stores.set_halign (Align.START);
225
data.grid.attach (stores, 1, 1, 1, 1);
230
data.grid.set_data<ContactData> ("data", data);
231
data.grid.show_all ();
232
contacts.set (c, data);
233
this.add (data.grid);
377
236
private void contact_removed_cb (Store store, Contact c) {
378
var data = lookup_data (c);
381
remove_from_model (data);
383
c.remove_lookup<ContactData> (this);
386
public bool lookup_iter (Contact c, out TreeIter iter) {
387
var data = lookup_data (c);
394
private CellRendererShape shape;
395
public enum TextDisplay {
400
private TextDisplay text_display;
402
public signal void selection_changed (Contact? contact);
404
private void init_view (TextDisplay text_display) {
405
this.text_display = text_display;
407
set_model (list_store);
408
set_headers_visible (false);
410
var row_padding = 12;
412
var selection = get_selection ();
413
selection.set_mode (SelectionMode.BROWSE);
414
selection.set_select_function ( (selection, model, path, path_currently_selected) => {
417
model.get_iter (out iter, path);
418
model.get (iter, 0, out contact);
419
return contact != null;
421
selection.changed.connect (contacts_selection_changed);
423
var column = new TreeViewColumn ();
424
column.set_spacing (8);
426
var icon = new CellRendererPixbuf ();
427
icon.set_padding (0, row_padding);
430
icon.width = Contact.SMALL_AVATAR_SIZE + 12;
431
column.pack_start (icon, false);
432
column.set_cell_data_func (icon, (column, cell, model, iter) => {
435
model.get (iter, 0, out contact);
437
if (contact == null) {
438
cell.set ("pixbuf", null);
439
cell.visible = false;
445
cell.set ("pixbuf", contact.small_avatar);
447
cell.set ("pixbuf", null);
450
shape = new CellRendererShape ();
451
shape.set_padding (0, row_padding);
453
Pango.cairo_context_set_shape_renderer (get_pango_context (), shape.render_shape);
455
column.pack_start (shape, true);
456
column.set_cell_data_func (shape, (column, cell, model, iter) => {
459
model.get (iter, 0, out contact);
461
if (contact == null) {
462
cell.visible = false;
467
var name = contact.display_name;
468
switch (text_display) {
470
case TextDisplay.NONE:
471
cell.set ("name", name,
472
"show_presence", false,
475
case TextDisplay.PRESENCE:
476
cell.set ("name", name,
477
"show_presence", true,
478
"presence", contact.presence_type,
479
"message", contact.presence_message,
480
"is_phone", contact.is_phone);
482
case TextDisplay.STORES:
483
string stores = contact.format_persona_stores ();
484
cell.set ("name", name,
485
"show_presence", false,
491
var text = new CellRendererText ();
492
text.set_alignment (0, 0);
493
column.pack_start (text, true);
494
text.set ("weight", Pango.Weight.BOLD);
495
column.set_cell_data_func (text, (column, cell, model, iter) => {
498
model.get (iter, 0, out contact);
499
cell.visible = contact == null;
501
string header = get_header_text (iter);
502
cell.set ("text", header);
504
cell.height = 6; // PADDING
510
append_column (column);
513
private void contacts_selection_changed (TreeSelection selection) {
517
Contact? contact = null;
518
if (selection.get_selected (out model, out iter)) {
519
model.get (iter, 0, out contact);
237
var data = contacts.get (c);
238
data.grid.destroy ();
239
data.label.destroy ();
240
data.image_frame.destroy ();
244
public override void child_selected (Widget? child) {
245
var data = child.get_data<ContactData> ("data");
246
var contact = data != null ? data.contact : null;
522
247
selection_changed (contact);
249
contact.fetch_contact_info ();
252
private bool filter (Widget child) {
253
var data = child.get_data<ContactData> ("data");
255
return data.filtered;
258
private void update_separator (ref Widget? separator,
260
Widget? before_widget) {
261
var w_data = widget.get_data<ContactData> ("data");
262
ContactData? before_data = null;
263
if (before_widget != null)
264
before_data = before_widget.get_data<ContactData> ("data");
266
if (before_data == null && w_data.sort_prio > 0) {
267
if (separator == null ||
268
!(separator.get_data<bool> ("contacts-suggestions-header"))) {
269
var l = new Label ("");
270
l.set_data ("contacts-suggestions-header", true);
271
l.set_markup (Markup.printf_escaped ("<b>%s</b>", _("Suggestions")));
272
l.set_halign (Align.START);
278
if (before_data != null && before_data.sort_prio > 0 &&
279
w_data.sort_prio == 0) {
280
if (separator == null ||
281
!(separator.get_data<bool> ("contacts-rest-header"))) {
282
var l = new Label ("");
283
l.set_data ("contacts-rest-header", true);
284
l.set_halign (Align.START);
290
if (is_other (w_data) &&
291
(before_data == null || !is_other (before_data))) {
292
if (separator == null ||
293
!(separator.get_data<bool> ("contacts-other-header"))) {
294
var l = new Label ("");
295
l.set_data ("contacts-other-header", true);
296
l.set_markup (Markup.printf_escaped ("<b>%s</b>", _("Other Contacts")));
297
l.set_halign (Align.START);
303
if (before_data != null &&
304
w_data.initial_letter != before_data.initial_letter) {
305
if (separator == null || !(separator is Separator))
306
separator = new Separator (Orientation.HORIZONTAL);
525
312
public void select_contact (Contact contact) {
527
if (lookup_iter (contact, out iter)) {
528
get_selection ().select_iter (iter);
529
scroll_to_cell (list_store.get_path (iter),
530
null, true, 0.0f, 0.0f);
313
var data = contacts.get (contact);
314
select_child (data.grid);