Initial use of ContactsSorted for view
This commit is contained in:
parent
02d79e9779
commit
bd8316edee
5 changed files with 180 additions and 415 deletions
|
@ -33,6 +33,7 @@ vala_sources = \
|
||||||
contacts-linking.vala \
|
contacts-linking.vala \
|
||||||
contacts-menu-button.vala \
|
contacts-menu-button.vala \
|
||||||
contacts-row.vala \
|
contacts-row.vala \
|
||||||
|
contacts-sorted.vala \
|
||||||
contacts-store.vala \
|
contacts-store.vala \
|
||||||
contacts-view.vala \
|
contacts-view.vala \
|
||||||
contacts-utils.vala \
|
contacts-utils.vala \
|
||||||
|
|
|
@ -220,7 +220,7 @@ public class Contacts.LinkDialog : Dialog {
|
||||||
scrolled.set_vexpand (true);
|
scrolled.set_vexpand (true);
|
||||||
scrolled.set_hexpand (true);
|
scrolled.set_hexpand (true);
|
||||||
scrolled.set_shadow_type (ShadowType.NONE);
|
scrolled.set_shadow_type (ShadowType.NONE);
|
||||||
scrolled.add (view);
|
scrolled.add_with_viewport (view);
|
||||||
list_grid.add (scrolled);
|
list_grid.add (scrolled);
|
||||||
|
|
||||||
view.selection_changed.connect ( (c) => {
|
view.selection_changed.connect ( (c) => {
|
||||||
|
|
|
@ -143,7 +143,7 @@ public class Contacts.ListPane : Frame {
|
||||||
selection_changed (contact);
|
selection_changed (contact);
|
||||||
});
|
});
|
||||||
|
|
||||||
scrolled.add (contacts_view);
|
scrolled.add_with_viewport (contacts_view);
|
||||||
contacts_view.show_all ();
|
contacts_view.show_all ();
|
||||||
scrolled.set_no_show_all (true);
|
scrolled.set_no_show_all (true);
|
||||||
|
|
||||||
|
@ -160,7 +160,6 @@ public class Contacts.ListPane : Frame {
|
||||||
public void select_contact (Contact contact, bool ignore_change = false) {
|
public void select_contact (Contact contact, bool ignore_change = false) {
|
||||||
if (ignore_change)
|
if (ignore_change)
|
||||||
ignore_selection_change = true;
|
ignore_selection_change = true;
|
||||||
contacts_view.select_contact (contact);
|
|
||||||
ignore_selection_change = false;
|
ignore_selection_change = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,11 +99,16 @@ public class Contacts.Sorted : Container {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
selected_child = child_info;
|
selected_child = child_info;
|
||||||
|
|
||||||
|
child_selected (selected_child != null ? selected_child.widget : null);
|
||||||
queue_draw ();
|
queue_draw ();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual signal void child_selected (Widget? child) {
|
||||||
|
}
|
||||||
|
|
||||||
public override bool draw (Cairo.Context cr) {
|
public override bool draw (Cairo.Context cr) {
|
||||||
Allocation allocation;
|
Allocation allocation;
|
||||||
this.get_allocation (out allocation);
|
this.get_allocation (out allocation);
|
||||||
|
@ -120,14 +125,14 @@ public class Contacts.Sorted : Container {
|
||||||
0, selected_child.y,
|
0, selected_child.y,
|
||||||
allocation.width, selected_child.height);
|
allocation.width, selected_child.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.restore ();
|
context.restore ();
|
||||||
|
|
||||||
base.draw (cr);
|
base.draw (cr);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void realize () {
|
public override void realize () {
|
||||||
Allocation allocation;
|
Allocation allocation;
|
||||||
get_allocation (out allocation);
|
get_allocation (out allocation);
|
||||||
|
@ -198,22 +203,54 @@ public class Contacts.Sorted : Container {
|
||||||
queue_resize ();
|
queue_resize ();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update_separator (SequenceIter<ChildInfo?> iter, SequenceIter<ChildInfo?>? before_iter,
|
private SequenceIter<ChildInfo?>? get_previous_visible (SequenceIter<ChildInfo?> _iter) {
|
||||||
bool update_if_exist) {
|
if (_iter.is_begin())
|
||||||
|
return null;
|
||||||
|
var iter = _iter;
|
||||||
|
|
||||||
|
do {
|
||||||
|
iter = iter.prev ();
|
||||||
|
|
||||||
|
unowned ChildInfo? child_info = iter.get ();
|
||||||
|
unowned Widget widget = child_info.widget;
|
||||||
|
if (widget.get_visible () && widget.get_child_visible ())
|
||||||
|
return iter;
|
||||||
|
} while (!iter.is_begin ());
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SequenceIter<ChildInfo?>? get_next_visible (SequenceIter<ChildInfo?> _iter) {
|
||||||
|
if (_iter.is_end())
|
||||||
|
return _iter;
|
||||||
|
|
||||||
|
var iter = _iter;
|
||||||
|
do {
|
||||||
|
iter = iter.next ();
|
||||||
|
|
||||||
|
if (!iter.is_end ()) {
|
||||||
|
unowned ChildInfo? child_info = iter.get ();
|
||||||
|
unowned Widget widget = child_info.widget;
|
||||||
|
if (widget.get_visible () && widget.get_child_visible ())
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
} while (!iter.is_end ());
|
||||||
|
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update_separator (SequenceIter<ChildInfo?> iter, bool update_if_exist) {
|
||||||
if (iter.is_end ())
|
if (iter.is_end ())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unowned ChildInfo? info = iter.get ();
|
unowned ChildInfo? info = iter.get ();
|
||||||
unowned ChildInfo? before_info = null;
|
var before_iter = get_previous_visible (iter);
|
||||||
if (!iter.is_begin()) {
|
|
||||||
if (before_iter == null)
|
|
||||||
before_info = iter.prev ().get ();
|
|
||||||
else
|
|
||||||
before_info = before_iter.get ();
|
|
||||||
}
|
|
||||||
|
|
||||||
var widget = info.widget;
|
var widget = info.widget;
|
||||||
Widget? before_widget = before_info != null ? before_info.widget : null;
|
Widget? before_widget = null;
|
||||||
|
if (before_iter != null) {
|
||||||
|
unowned ChildInfo? before_info = before_iter.get ();
|
||||||
|
before_widget = before_info.widget;
|
||||||
|
}
|
||||||
|
|
||||||
bool need_separator = false;
|
bool need_separator = false;
|
||||||
|
|
||||||
|
@ -244,10 +281,8 @@ public class Contacts.Sorted : Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reseparate () {
|
public void reseparate () {
|
||||||
SequenceIter<ChildInfo?>? last = null;
|
|
||||||
for (var iter = children.get_begin_iter (); !iter.is_end (); iter = iter.next ()) {
|
for (var iter = children.get_begin_iter (); !iter.is_end (); iter = iter.next ()) {
|
||||||
update_separator (iter, last, false);
|
update_separator (iter, false);
|
||||||
last = iter;
|
|
||||||
}
|
}
|
||||||
queue_resize ();
|
queue_resize ();
|
||||||
}
|
}
|
||||||
|
@ -285,10 +320,10 @@ public class Contacts.Sorted : Container {
|
||||||
|
|
||||||
apply_filter (widget);
|
apply_filter (widget);
|
||||||
|
|
||||||
var prev_next = iter.next ();
|
var prev_next = get_next_visible (iter);
|
||||||
update_separator (iter, null, true);
|
update_separator (iter, true);
|
||||||
update_separator (iter.next (), iter, true);
|
update_separator (get_next_visible (iter), true);
|
||||||
update_separator (prev_next, null, true);
|
update_separator (prev_next, true);
|
||||||
|
|
||||||
info.iter = iter;
|
info.iter = iter;
|
||||||
|
|
||||||
|
@ -300,16 +335,16 @@ public class Contacts.Sorted : Container {
|
||||||
if (info == null)
|
if (info == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var prev_next = info.iter.next ();
|
var prev_next = get_previous_visible (info.iter);
|
||||||
|
|
||||||
if (sort_func != null) {
|
if (sort_func != null) {
|
||||||
children.sort_changed (info.iter, do_sort);
|
children.sort_changed (info.iter, do_sort);
|
||||||
this.queue_resize ();
|
this.queue_resize ();
|
||||||
}
|
}
|
||||||
apply_filter (info.widget);
|
apply_filter (info.widget);
|
||||||
update_separator (info.iter, null, true);
|
update_separator (info.iter, true);
|
||||||
update_separator (info.iter.next (), info.iter, true);
|
update_separator (get_next_visible (info.iter), true);
|
||||||
update_separator (prev_next, null, true);
|
update_separator (prev_next, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,14 +353,14 @@ public class Contacts.Sorted : Container {
|
||||||
if (info == null)
|
if (info == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var next = info.iter.next ();
|
var next = get_next_visible (info.iter);
|
||||||
|
|
||||||
bool was_visible = widget.get_visible ();
|
bool was_visible = widget.get_visible ();
|
||||||
widget.unparent ();
|
widget.unparent ();
|
||||||
|
|
||||||
child_hash.unset (widget);
|
child_hash.unset (widget);
|
||||||
|
|
||||||
update_separator (next, null, false);
|
update_separator (next, false);
|
||||||
|
|
||||||
if (was_visible && this.get_visible ())
|
if (was_visible && this.get_visible ())
|
||||||
this.queue_resize ();
|
this.queue_resize ();
|
||||||
|
@ -371,6 +406,11 @@ public class Contacts.Sorted : Container {
|
||||||
if (!widget.get_visible () || !widget.get_child_visible ())
|
if (!widget.get_visible () || !widget.get_child_visible ())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (child_info.separator != null) {
|
||||||
|
child_info.separator.get_preferred_height_for_width (width, out child_min, null);
|
||||||
|
minimum_height += child_min;
|
||||||
|
}
|
||||||
|
|
||||||
widget.get_preferred_height_for_width (width, out child_min, null);
|
widget.get_preferred_height_for_width (width, out child_min, null);
|
||||||
minimum_height += child_min;
|
minimum_height += child_min;
|
||||||
}
|
}
|
||||||
|
@ -429,8 +469,11 @@ public class Contacts.Sorted : Container {
|
||||||
unowned Widget widget = child_info.widget;
|
unowned Widget widget = child_info.widget;
|
||||||
int child_min;
|
int child_min;
|
||||||
|
|
||||||
if (!widget.get_visible () || !widget.get_child_visible ())
|
if (!widget.get_visible () || !widget.get_child_visible ()) {
|
||||||
|
child_info.y = child_allocation.y;
|
||||||
|
child_info.height = 0;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (child_info.separator != null) {
|
if (child_info.separator != null) {
|
||||||
child_info.separator.get_preferred_height_for_width (allocation.width, out child_min, null);
|
child_info.separator.get_preferred_height_for_width (allocation.width, out child_min, null);
|
||||||
|
|
|
@ -20,13 +20,16 @@ using Gtk;
|
||||||
using Folks;
|
using Folks;
|
||||||
using Gee;
|
using Gee;
|
||||||
|
|
||||||
public class Contacts.View : TreeView {
|
public class Contacts.View : Contacts.Sorted {
|
||||||
private class ContactData {
|
private class ContactData {
|
||||||
public Contact contact;
|
public Contact contact;
|
||||||
public TreeIter iter;
|
public Grid grid;
|
||||||
public bool visible;
|
public Label label;
|
||||||
public bool is_first;
|
public ContactFrame image_frame;
|
||||||
public int sort_prio;
|
public int sort_prio;
|
||||||
|
public string display_name;
|
||||||
|
public unichar initial_letter;
|
||||||
|
public bool filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Subset {
|
public enum Subset {
|
||||||
|
@ -36,46 +39,48 @@ public class Contacts.View : TreeView {
|
||||||
ALL
|
ALL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum TextDisplay {
|
||||||
|
NONE,
|
||||||
|
PRESENCE,
|
||||||
|
STORES
|
||||||
|
}
|
||||||
|
|
||||||
|
public signal void selection_changed (Contact? contact);
|
||||||
|
|
||||||
Store contacts_store;
|
Store contacts_store;
|
||||||
Subset show_subset;
|
Subset show_subset;
|
||||||
ListStore list_store;
|
HashMap<Contact,ContactData> contacts;
|
||||||
HashSet<Contact> hidden_contacts;
|
HashSet<Contact> hidden_contacts;
|
||||||
|
|
||||||
string []? filter_values;
|
string []? filter_values;
|
||||||
int custom_visible_count;
|
int custom_visible_count;
|
||||||
ContactData suggestions_header_data;
|
ContactData suggestions_header_data;
|
||||||
ContactData padding_data;
|
ContactData padding_data;
|
||||||
ContactData other_header_data;
|
ContactData other_header_data;
|
||||||
|
private TextDisplay text_display;
|
||||||
|
|
||||||
public View (Store store, TextDisplay text_display = TextDisplay.PRESENCE) {
|
public View (Store store, TextDisplay text_display = TextDisplay.PRESENCE) {
|
||||||
contacts_store = store;
|
contacts_store = store;
|
||||||
hidden_contacts = new HashSet<Contact>();
|
hidden_contacts = new HashSet<Contact>();
|
||||||
show_subset = Subset.ALL;
|
show_subset = Subset.ALL;
|
||||||
|
|
||||||
list_store = new ListStore (2, typeof (Contact), typeof (ContactData *));
|
contacts = new HashMap<Contact,ContactData> ();
|
||||||
suggestions_header_data = new ContactData ();
|
|
||||||
suggestions_header_data.sort_prio = int.MAX;
|
|
||||||
padding_data = new ContactData ();
|
|
||||||
padding_data.sort_prio = 1;
|
|
||||||
|
|
||||||
other_header_data = new ContactData ();
|
this.set_sort_func ((widget_a, widget_b) => {
|
||||||
other_header_data.sort_prio = -1;
|
var a = widget_a.get_data<ContactData> ("data");
|
||||||
|
var b = widget_b.get_data<ContactData> ("data");
|
||||||
list_store.set_sort_func (0, (model, iter_a, iter_b) => {
|
return compare_data (a, b);
|
||||||
ContactData *aa, bb;
|
|
||||||
model.get (iter_a, 1, out aa);
|
|
||||||
model.get (iter_b, 1, out bb);
|
|
||||||
|
|
||||||
return compare_data (aa, bb);
|
|
||||||
});
|
});
|
||||||
list_store.set_sort_column_id (0, SortType.ASCENDING);
|
this.set_filter_func (filter);
|
||||||
|
this.set_separator_funcs (need_separator,
|
||||||
|
create_separator,
|
||||||
|
update_separator);
|
||||||
|
|
||||||
contacts_store.added.connect (contact_added_cb);
|
contacts_store.added.connect (contact_added_cb);
|
||||||
contacts_store.removed.connect (contact_removed_cb);
|
contacts_store.removed.connect (contact_removed_cb);
|
||||||
contacts_store.changed.connect (contact_changed_cb);
|
contacts_store.changed.connect (contact_changed_cb);
|
||||||
foreach (var c in store.get_contacts ())
|
foreach (var c in store.get_contacts ())
|
||||||
contact_added_cb (store, c);
|
contact_added_cb (store, c);
|
||||||
|
|
||||||
init_view (text_display);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int compare_data (ContactData a_data, ContactData b_data) {
|
private int compare_data (ContactData a_data, ContactData b_data) {
|
||||||
|
@ -90,13 +95,13 @@ public class Contacts.View : TreeView {
|
||||||
var a = a_data.contact;
|
var a = a_data.contact;
|
||||||
var b = b_data.contact;
|
var b = b_data.contact;
|
||||||
|
|
||||||
if (is_set (a.display_name) && is_set (b.display_name))
|
if (is_set (a_data.display_name) && is_set (b_data.display_name))
|
||||||
return a.display_name.collate (b.display_name);
|
return a.display_name.collate (b_data.display_name);
|
||||||
|
|
||||||
// Sort empty names last
|
// Sort empty names last
|
||||||
if (is_set (a.display_name))
|
if (is_set (a_data.display_name))
|
||||||
return -1;
|
return -1;
|
||||||
if (is_set (b.display_name))
|
if (is_set (b_data.display_name))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -121,36 +126,9 @@ public class Contacts.View : TreeView {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string get_header_text (TreeIter iter) {
|
|
||||||
ContactData *data;
|
|
||||||
list_store.get (iter, 1, out data);
|
|
||||||
if (data == suggestions_header_data) {
|
|
||||||
/* Translators: This is the header for the list of suggested contacts to
|
|
||||||
link to the current contact */
|
|
||||||
return ngettext ("Suggestion", "Suggestions", custom_visible_count);
|
|
||||||
}
|
|
||||||
if (data == other_header_data) {
|
|
||||||
/* Translators: This is the header for the list of suggested contacts to
|
|
||||||
link to the current contact */
|
|
||||||
return _("Other Contacts");
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set_show_subset (Subset subset) {
|
public void set_show_subset (Subset subset) {
|
||||||
show_subset = subset;
|
show_subset = subset;
|
||||||
|
update_all_filtered ();
|
||||||
bool new_visible = show_subset == Subset.ALL_SEPARATED;
|
|
||||||
if (new_visible && !other_header_data.visible) {
|
|
||||||
other_header_data.visible = true;
|
|
||||||
list_store.append (out other_header_data.iter);
|
|
||||||
list_store.set (other_header_data.iter, 1, other_header_data);
|
|
||||||
}
|
|
||||||
if (!new_visible && other_header_data.visible) {
|
|
||||||
other_header_data.visible = false;
|
|
||||||
list_store.remove (other_header_data.iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
refilter ();
|
refilter ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,376 +136,120 @@ public class Contacts.View : TreeView {
|
||||||
/* We use negative prios internally */
|
/* We use negative prios internally */
|
||||||
assert (prio >= 0);
|
assert (prio >= 0);
|
||||||
|
|
||||||
var data = lookup_data (c);
|
var data = contacts.get (c);
|
||||||
|
|
||||||
if (data == null)
|
if (data == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We insert a priority between 0 and 1 for the padding
|
|
||||||
if (prio > 0)
|
|
||||||
prio += 1;
|
|
||||||
data.sort_prio = prio;
|
|
||||||
contact_changed_cb (contacts_store, c);
|
|
||||||
|
|
||||||
if (data.visible) {
|
|
||||||
if (prio > 0) {
|
|
||||||
if (custom_visible_count++ == 0)
|
|
||||||
add_custom_headers ();
|
|
||||||
} else {
|
|
||||||
if (custom_visible_count-- == 1)
|
|
||||||
remove_custom_headers ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool apply_filter (Contact contact) {
|
|
||||||
if (contact.is_hidden)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (contact in hidden_contacts)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ((show_subset == Subset.MAIN &&
|
|
||||||
!contact.is_main) ||
|
|
||||||
(show_subset == Subset.OTHER &&
|
|
||||||
contact.is_main))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (filter_values == null || filter_values.length == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return contact.contains_strings (filter_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool is_first (TreeIter iter) {
|
|
||||||
ContactData *data;
|
|
||||||
list_store.get (iter, 1, out data);
|
|
||||||
if (data != null)
|
|
||||||
return data->is_first;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ContactData? get_previous (ContactData data) {
|
|
||||||
ContactData *previous = null;
|
|
||||||
TreeIter iter = data.iter;
|
|
||||||
if (list_store.iter_previous (ref iter))
|
|
||||||
list_store.get (iter, 1, out previous);
|
|
||||||
return previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ContactData? get_next (ContactData data) {
|
|
||||||
ContactData *next = null;
|
|
||||||
TreeIter iter = data.iter;
|
|
||||||
if (list_store.iter_next (ref iter))
|
|
||||||
list_store.get (iter, 1, out next);
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void row_changed_no_resort (ContactData data) {
|
|
||||||
var path = list_store.get_path (data.iter);
|
|
||||||
list_store.row_changed (path, data.iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void row_changed_resort (ContactData data) {
|
|
||||||
list_store.set (data.iter, 0, data.contact);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool update_is_first (ContactData data, ContactData? previous) {
|
|
||||||
bool old_is_first = data.is_first;
|
|
||||||
|
|
||||||
bool is_custom = data.sort_prio != 0;
|
|
||||||
bool previous_is_custom = previous != null && (previous.sort_prio != 0) ;
|
|
||||||
|
|
||||||
if (is_custom) {
|
|
||||||
data.is_first = false;
|
|
||||||
} else if (previous != null && !previous_is_custom) {
|
|
||||||
unichar previous_initial = previous.contact.initial_letter;
|
|
||||||
unichar initial = data.contact.initial_letter;
|
|
||||||
data.is_first = previous_initial != initial;
|
|
||||||
} else {
|
|
||||||
data.is_first = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old_is_first != data.is_first) {
|
|
||||||
row_changed_no_resort (data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add_custom_headers () {
|
|
||||||
suggestions_header_data.visible = true;
|
|
||||||
list_store.append (out suggestions_header_data.iter);
|
|
||||||
list_store.set (suggestions_header_data.iter, 1, suggestions_header_data);
|
|
||||||
padding_data.visible = true;
|
|
||||||
list_store.append (out padding_data.iter);
|
|
||||||
list_store.set (padding_data.iter, 1, padding_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void remove_custom_headers () {
|
|
||||||
suggestions_header_data.visible = false;
|
|
||||||
list_store.remove (suggestions_header_data.iter);
|
|
||||||
padding_data.visible = false;
|
|
||||||
list_store.remove (padding_data.iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add_to_model (ContactData data) {
|
|
||||||
list_store.append (out data.iter);
|
|
||||||
list_store.set (data.iter, 0, data.contact, 1, data);
|
|
||||||
|
|
||||||
if (data.sort_prio > 0) {
|
|
||||||
if (custom_visible_count++ == 0)
|
|
||||||
add_custom_headers ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (update_is_first (data, get_previous (data)) && data.is_first) {
|
|
||||||
/* The newly added row is first, the next one might not be anymore */
|
|
||||||
var next = get_next (data);
|
|
||||||
if (next != null)
|
|
||||||
update_is_first (next, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void remove_from_model (ContactData data) {
|
|
||||||
if (data.sort_prio > 0) {
|
|
||||||
if (custom_visible_count-- == 1)
|
|
||||||
remove_custom_headers ();
|
|
||||||
}
|
|
||||||
|
|
||||||
ContactData? next = null;
|
|
||||||
if (data.is_first)
|
|
||||||
next = get_next (data);
|
|
||||||
|
|
||||||
list_store.remove (data.iter);
|
|
||||||
data.is_first = false;
|
|
||||||
|
|
||||||
if (next != null)
|
|
||||||
update_is_first (next, get_previous (next));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void update_visible (ContactData data) {
|
|
||||||
bool was_visible = data.visible;
|
|
||||||
data.visible = apply_filter (data.contact);
|
|
||||||
|
|
||||||
if (was_visible && !data.visible)
|
|
||||||
remove_from_model (data);
|
|
||||||
|
|
||||||
if (!was_visible && data.visible)
|
|
||||||
add_to_model (data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refilter () {
|
|
||||||
foreach (var c in contacts_store.get_contacts ()) {
|
|
||||||
update_visible (lookup_data (c));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hide_contact (Contact contact) {
|
public void hide_contact (Contact contact) {
|
||||||
hidden_contacts.add (contact);
|
hidden_contacts.add (contact);
|
||||||
|
update_all_filtered ();
|
||||||
refilter ();
|
refilter ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set_filter_values (string []? values) {
|
public void set_filter_values (string []? values) {
|
||||||
filter_values = values;
|
filter_values = values;
|
||||||
|
update_all_filtered ();
|
||||||
refilter ();
|
refilter ();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void contact_changed_cb (Store store, Contact c) {
|
private bool calculate_filtered (Contact c) {
|
||||||
ContactData data = lookup_data (c);
|
if (c.is_hidden)
|
||||||
|
return false;
|
||||||
|
|
||||||
bool was_visible = data.visible;
|
if (c in hidden_contacts)
|
||||||
|
return false;
|
||||||
|
|
||||||
ContactData? next = null;
|
if ((show_subset == Subset.MAIN &&
|
||||||
if (data.visible)
|
!c.is_main) ||
|
||||||
next = get_next (data);
|
(show_subset == Subset.OTHER &&
|
||||||
|
c.is_main))
|
||||||
|
return false;
|
||||||
|
|
||||||
update_visible (data);
|
if (filter_values == null || filter_values.length == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (was_visible && data.visible) {
|
return c.contains_strings (filter_values);
|
||||||
/* We just moved position in the list while visible */
|
}
|
||||||
|
|
||||||
row_changed_resort (data);
|
private void update_data (ContactData data) {
|
||||||
|
var c = data.contact;
|
||||||
|
data.display_name = c.display_name;
|
||||||
|
data.initial_letter = c.initial_letter;
|
||||||
|
data.filtered = calculate_filtered (c);
|
||||||
|
|
||||||
/* Update the is_first on the previous next row */
|
data.label.set_text (data.display_name);
|
||||||
if (next != null)
|
data.image_frame.set_image (c.individual, c);
|
||||||
update_is_first (next, get_previous (next));
|
}
|
||||||
|
|
||||||
/* Update the is_first on the new next row */
|
private void update_all_filtered () {
|
||||||
next = get_next (data);
|
foreach (var data in contacts.values) {
|
||||||
if (next != null)
|
data.filtered = calculate_filtered (data.contact);
|
||||||
update_is_first (next, data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContactData lookup_data (Contact c) {
|
private void contact_changed_cb (Store store, Contact c) {
|
||||||
return c.lookup<ContactData> (this);
|
var data = contacts.get (c);
|
||||||
|
update_data (data);
|
||||||
|
child_changed (data.grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void contact_added_cb (Store store, Contact c) {
|
private void contact_added_cb (Store store, Contact c) {
|
||||||
ContactData data = new ContactData();
|
var data = new ContactData();
|
||||||
data.contact = c;
|
data.contact = c;
|
||||||
data.visible = false;
|
data.grid = new Grid ();
|
||||||
|
data.image_frame = new ContactFrame (Contact.SMALL_AVATAR_SIZE);
|
||||||
|
data.label = new Label ("");
|
||||||
|
data.grid.add (data.image_frame);
|
||||||
|
data.grid.add (data.label);
|
||||||
|
|
||||||
c.set_lookup (this, data);
|
update_data (data);
|
||||||
|
|
||||||
update_visible (data);
|
data.grid.set_data<ContactData> ("data", data);
|
||||||
|
data.grid.show_all ();
|
||||||
|
contacts.set (c, data);
|
||||||
|
this.add (data.grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void contact_removed_cb (Store store, Contact c) {
|
private void contact_removed_cb (Store store, Contact c) {
|
||||||
var data = lookup_data (c);
|
var data = contacts.get (c);
|
||||||
|
data.grid.destroy ();
|
||||||
if (data.visible)
|
data.label.destroy ();
|
||||||
remove_from_model (data);
|
data.image_frame.destroy ();
|
||||||
|
this.remove (data.grid);
|
||||||
c.remove_lookup<ContactData> (this);
|
contacts.unset (c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool lookup_iter (Contact c, out TreeIter iter) {
|
public override void child_selected (Widget? child) {
|
||||||
var data = lookup_data (c);
|
var data = child.get_data<ContactData> ("data");
|
||||||
iter = data.iter;
|
selection_changed (data != null ? data.contact : null);
|
||||||
return data.visible;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool need_separator (Widget widget, Widget? before) {
|
||||||
|
if (before == null) {
|
||||||
private CellRendererShape shape;
|
return true;
|
||||||
public enum TextDisplay {
|
|
||||||
NONE,
|
|
||||||
PRESENCE,
|
|
||||||
STORES
|
|
||||||
}
|
|
||||||
private TextDisplay text_display;
|
|
||||||
|
|
||||||
public signal void selection_changed (Contact? contact);
|
|
||||||
|
|
||||||
private void init_view (TextDisplay text_display) {
|
|
||||||
this.text_display = text_display;
|
|
||||||
|
|
||||||
set_model (list_store);
|
|
||||||
set_headers_visible (false);
|
|
||||||
|
|
||||||
var row_padding = 12;
|
|
||||||
|
|
||||||
var selection = get_selection ();
|
|
||||||
selection.set_mode (SelectionMode.BROWSE);
|
|
||||||
selection.set_select_function ( (selection, model, path, path_currently_selected) => {
|
|
||||||
Contact contact;
|
|
||||||
TreeIter iter;
|
|
||||||
model.get_iter (out iter, path);
|
|
||||||
model.get (iter, 0, out contact);
|
|
||||||
return contact != null;
|
|
||||||
});
|
|
||||||
selection.changed.connect (contacts_selection_changed);
|
|
||||||
|
|
||||||
var column = new TreeViewColumn ();
|
|
||||||
column.set_spacing (8);
|
|
||||||
|
|
||||||
var icon = new CellRendererPixbuf ();
|
|
||||||
icon.set_padding (0, row_padding);
|
|
||||||
icon.xalign = 1.0f;
|
|
||||||
icon.yalign = 0.0f;
|
|
||||||
icon.width = Contact.SMALL_AVATAR_SIZE + 12;
|
|
||||||
column.pack_start (icon, false);
|
|
||||||
column.set_cell_data_func (icon, (column, cell, model, iter) => {
|
|
||||||
Contact contact;
|
|
||||||
|
|
||||||
model.get (iter, 0, out contact);
|
|
||||||
|
|
||||||
if (contact == null) {
|
|
||||||
cell.set ("pixbuf", null);
|
|
||||||
cell.visible = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cell.visible = true;
|
|
||||||
|
|
||||||
if (contact != null)
|
|
||||||
cell.set ("pixbuf", contact.small_avatar);
|
|
||||||
else
|
|
||||||
cell.set ("pixbuf", null);
|
|
||||||
});
|
|
||||||
|
|
||||||
shape = new CellRendererShape ();
|
|
||||||
shape.set_padding (0, row_padding);
|
|
||||||
|
|
||||||
Pango.cairo_context_set_shape_renderer (get_pango_context (), shape.render_shape);
|
|
||||||
|
|
||||||
column.pack_start (shape, true);
|
|
||||||
column.set_cell_data_func (shape, (column, cell, model, iter) => {
|
|
||||||
Contact contact;
|
|
||||||
|
|
||||||
model.get (iter, 0, out contact);
|
|
||||||
|
|
||||||
if (contact == null) {
|
|
||||||
cell.visible = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cell.visible = true;
|
|
||||||
|
|
||||||
var name = contact.display_name;
|
|
||||||
switch (text_display) {
|
|
||||||
default:
|
|
||||||
case TextDisplay.NONE:
|
|
||||||
cell.set ("name", name,
|
|
||||||
"show_presence", false,
|
|
||||||
"message", "");
|
|
||||||
break;
|
|
||||||
case TextDisplay.PRESENCE:
|
|
||||||
cell.set ("name", name,
|
|
||||||
"show_presence", true,
|
|
||||||
"presence", contact.presence_type,
|
|
||||||
"message", contact.presence_message,
|
|
||||||
"is_phone", contact.is_phone);
|
|
||||||
break;
|
|
||||||
case TextDisplay.STORES:
|
|
||||||
string stores = contact.format_persona_stores ();
|
|
||||||
cell.set ("name", name,
|
|
||||||
"show_presence", false,
|
|
||||||
"message", stores);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var text = new CellRendererText ();
|
|
||||||
text.set_alignment (0, 0);
|
|
||||||
column.pack_start (text, true);
|
|
||||||
text.set ("weight", Pango.Weight.BOLD);
|
|
||||||
column.set_cell_data_func (text, (column, cell, model, iter) => {
|
|
||||||
Contact contact;
|
|
||||||
|
|
||||||
model.get (iter, 0, out contact);
|
|
||||||
cell.visible = contact == null;
|
|
||||||
if (cell.visible) {
|
|
||||||
string header = get_header_text (iter);
|
|
||||||
cell.set ("text", header);
|
|
||||||
if (header == "")
|
|
||||||
cell.height = 6; // PADDING
|
|
||||||
else
|
|
||||||
cell.height = -1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
append_column (column);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void contacts_selection_changed (TreeSelection selection) {
|
|
||||||
TreeIter iter;
|
|
||||||
TreeModel model;
|
|
||||||
|
|
||||||
Contact? contact = null;
|
|
||||||
if (selection.get_selected (out model, out iter)) {
|
|
||||||
model.get (iter, 0, out contact);
|
|
||||||
}
|
}
|
||||||
|
var w_data = widget.get_data<ContactData> ("data");
|
||||||
|
var before_data = before.get_data<ContactData> ("data");
|
||||||
|
|
||||||
selection_changed (contact);
|
return w_data.initial_letter != before_data.initial_letter;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void select_contact (Contact contact) {
|
private Widget create_separator () {
|
||||||
TreeIter iter;
|
var s = new Label ("---------------------");
|
||||||
if (lookup_iter (contact, out iter)) {
|
return s;
|
||||||
get_selection ().select_iter (iter);
|
}
|
||||||
scroll_to_cell (list_store.get_path (iter),
|
|
||||||
null, true, 0.0f, 0.0f);
|
private bool filter (Widget child) {
|
||||||
}
|
var data = child.get_data<ContactData> ("data");
|
||||||
|
|
||||||
|
return data.filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update_separator (Widget separator,
|
||||||
|
Widget child,
|
||||||
|
Widget? before_widget) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue