[gnome-contacts/wip/sorted: 1/8] Initial version of ContactsSorted
- From: Alexander Larsson <alexl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-contacts/wip/sorted: 1/8] Initial version of ContactsSorted
- Date: Wed, 9 May 2012 10:10:36 +0000 (UTC)
commit ec2939c8a9e3d30adf480ec9d5da202d8ee307f1
Author: Alexander Larsson <alexl redhat com>
Date: Fri Feb 17 16:11:57 2012 +0100
Initial version of ContactsSorted
src/Makefile.am | 10 ++-
src/contacts-sorted.vala | 272 ++++++++++++++++++++++++++++++++++++++++++++++
src/test-sorted.vala | 108 ++++++++++++++++++
3 files changed, 389 insertions(+), 1 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 3fc3dfd..f8423da 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,6 +7,7 @@ AM_CPPFLAGS = \
-DPKGDATADIR=\""$(pkgdatadir)"\" \
-DPKGLIBDIR=\""$(pkglibdir)"\" \
-DGNOME_DESKTOP_USE_UNSTABLE_API \
+ -Dget_preferred_height_for_width_internal=get_preferred_height_for_width
$(NULL)
AM_VALAFLAGS = \
@@ -19,7 +20,7 @@ AM_VALAFLAGS += -D HAVE_GSTREAMER @CONTACTS_GSTREAMER_PACKAGES@
AM_CPPFLAGS += $(CONTACTS_GSTREAMER_CFLAGS)
endif
-bin_PROGRAMS = gnome-contacts
+bin_PROGRAMS = gnome-contacts test-sorted
vala_sources = \
contacts-app.vala \
@@ -67,6 +68,13 @@ gnome_contacts_SOURCES = \
$(vala_sources) \
$(NULL)
+test_sorted_SOURCES = \
+ contacts-sorted.vala \
+ test-sorted.vala \
+ $(NULL)
+test_sorted_LDADD = $(CONTACTS_LIBS)
+
+
gnome_contacts_LDADD = $(CONTACTS_LIBS) -lm
if USE_GSTREAMER
diff --git a/src/contacts-sorted.vala b/src/contacts-sorted.vala
new file mode 100644
index 0000000..284108f
--- /dev/null
+++ b/src/contacts-sorted.vala
@@ -0,0 +1,272 @@
+/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2011 Alexander Larsson <alexl redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using Gtk;
+using Gee;
+
+/* Requriements:
+ + sort
+ + filter
+ + first char or type custom "separators"
+ (create, destroy, update)
+ + Work with largish sets of children
+
+ filter => child visibility setting
+
+ Q:
+ How to construct separators?
+ What about resort a single item, can be problem if more change
+ at the same time, need a stable sort...
+
+ settings:
+ sort function
+ filter function
+ needs_separator function
+ create_separator
+ update_separator (if the child below it changes)
+
+ ops:
+ child-changed (resort, refilter,
+ resort-all
+ refilter-all
+
+ Impl:
+ GSequence for children
+ GHashTable for child to iter mapping
+*/
+
+public class Contacts.Sorted : Container {
+ public delegate bool FilterFunc (Widget child);
+ public delegate bool NeedSeparatorFunc (Widget? before, Widget widget);
+ public delegate Widget CreateSeparatorFunc (Widget child);
+ public delegate void UpdateSeparatorFunc (Widget separator, Widget child);
+
+ struct ChildInfo {
+ Widget widget;
+ bool is_separator;
+ bool has_separator;
+ int height;
+ SequenceIter<ChildInfo?> iter;
+ }
+
+ Sequence<ChildInfo?> children;
+ HashMap<unowned Widget, unowned ChildInfo?> child_hash;
+ CompareDataFunc<Widget>? sort_func;
+ FilterFunc? filter_func;
+ NeedSeparatorFunc? need_separator_func;
+ CreateSeparatorFunc? create_separator_func;
+
+ private int do_sort (ChildInfo? a, ChildInfo? b) {
+ return sort_func (a.widget, b.widget);
+ }
+
+ public Sorted () {
+ set_has_window (false);
+ set_redraw_on_allocate (false);
+
+ children = new Sequence<ChildInfo?>();
+ child_hash = new HashMap<unowned Widget, unowned ChildInfo?> ();
+ }
+
+ private void apply_filter (Widget child) {
+ bool do_show = true;
+ if (filter_func != null)
+ do_show = filter_func (child);
+ child.set_child_visible (do_show);
+ }
+
+ private void apply_filter_all () {
+ for (var iter = children.get_begin_iter (); !iter.is_end (); iter = iter.next ()) {
+ unowned ChildInfo? child_info = iter.get ();
+ apply_filter (child_info.widget);
+ }
+ }
+
+ public void set_filter_func (owned FilterFunc? f) {
+ filter_func = (owned)f;
+ refilter ();
+ }
+
+ public void refilter () {
+ apply_filter_all ();
+ queue_resize ();
+ }
+
+ public void resort () {
+ children.sort (do_sort);
+ queue_resize ();
+ }
+
+ public void set_sort_func (owned CompareDataFunc<Widget>? f) {
+ sort_func = (owned)f;
+ resort ();
+ }
+
+ public override void map () {
+ base.map ();
+ }
+
+ public override void unmap () {
+ base.unmap ();
+ }
+
+ private unowned ChildInfo? lookup_info (Widget widget) {
+ return child_hash.get (widget);
+ }
+
+ public override void add (Widget widget) {
+ ChildInfo? the_info = { widget };
+ unowned ChildInfo? info = the_info;
+ SequenceIter<ChildInfo?> iter;
+
+ child_hash.set (widget, info);
+
+ if (sort_func != null)
+ iter = children.insert_sorted ((owned) the_info, do_sort);
+ else
+ iter = children.append ((owned) the_info);
+
+ apply_filter (widget);
+
+ info.iter = iter;
+
+ widget.set_parent (this);
+ }
+
+ public void child_changed (Widget widget) {
+ unowned ChildInfo? info = lookup_info (widget);
+ if (info == null)
+ return;
+
+ if (sort_func != null) {
+ children.sort_changed (info.iter, do_sort);
+ this.queue_resize ();
+ }
+ apply_filter (info.widget);
+ }
+
+ public override void remove (Widget widget) {
+ unowned ChildInfo? info = lookup_info (widget);
+ if (info == null)
+ return;
+
+ bool was_visible = widget.get_visible ();
+ widget.unparent ();
+
+ child_hash.unset (widget);
+
+ if (was_visible && this.get_visible ())
+ this.queue_resize ();
+ }
+
+ public override void forall_internal (bool include_internals,
+ Gtk.Callback callback) {
+ for (var iter = children.get_begin_iter (); !iter.is_end (); iter = iter.next ()) {
+ unowned ChildInfo? child_info = iter.get ();
+ callback (child_info.widget);
+ }
+ }
+
+ public override void compute_expand_internal (out bool hexpand, out bool vexpand) {
+ base.compute_expand_internal (out hexpand, out vexpand);
+ /* We don't expand vertically beyound the minimum size */
+ vexpand = false;
+ }
+
+ public override Type child_type () {
+ return typeof (Widget);
+ }
+
+ public override Gtk.SizeRequestMode get_request_mode () {
+ return SizeRequestMode.HEIGHT_FOR_WIDTH;
+ }
+
+ public override void get_preferred_height (out int minimum_height, out int natural_height) {
+ int natural_width;
+ get_preferred_width (null, out natural_width);
+ get_preferred_height_for_width_internal (natural_width, out minimum_height, out natural_height);
+ }
+
+ public override void get_preferred_height_for_width (int width, out int minimum_height, out int natural_height) {
+ minimum_height = 0;
+ for (var iter = children.get_begin_iter (); !iter.is_end (); iter = iter.next ()) {
+ unowned ChildInfo? child_info = iter.get ();
+ unowned Widget widget = child_info.widget;
+ int child_min;
+
+ if (!widget.get_visible () || !widget.get_child_visible ())
+ continue;
+
+ widget.get_preferred_height_for_width (width, out child_min, null);
+ minimum_height += child_min;
+ }
+ /* We always allocate the minimum height, since handling
+ expanding rows is way too costly, and unlikely to
+ be used, as lists are generally put inside a scrolling window
+ anyway.
+ */
+ natural_height = minimum_height;
+ }
+
+ public override void get_preferred_width (out int minimum_width, out int natural_width) {
+ minimum_width = 0;
+ natural_width = 0;
+ for (var iter = children.get_begin_iter (); !iter.is_end (); iter = iter.next ()) {
+ unowned ChildInfo? child_info = iter.get ();
+ unowned Widget widget = child_info.widget;
+ int child_min, child_nat;
+
+ if (!widget.get_visible () || !widget.get_child_visible ())
+ continue;
+
+ widget.get_preferred_width (out child_min, out child_nat);
+ minimum_width = int.max (minimum_width, child_min);
+ natural_width = int.max (natural_width, child_nat);
+ }
+ }
+
+ public override void get_preferred_width_for_height (int height, out int minimum_width, out int natural_width) {
+ get_preferred_width (out minimum_width, out natural_width);
+ }
+
+ public override void size_allocate (Gtk.Allocation allocation) {
+ Allocation child_allocation = { 0, 0, 0, 0};
+
+ set_allocation (allocation);
+
+ child_allocation.x = allocation.x;
+ child_allocation.y = allocation.y;
+ child_allocation.width = allocation.width;
+
+ for (var iter = children.get_begin_iter (); !iter.is_end (); iter = iter.next ()) {
+ unowned ChildInfo? child_info = iter.get ();
+ unowned Widget widget = child_info.widget;
+ int child_min;
+
+ if (!widget.get_visible () || !widget.get_child_visible ())
+ continue;
+
+ widget.get_preferred_height_for_width (allocation.width, out child_min, null);
+ child_allocation.height = child_info.height = child_min;
+
+ widget.size_allocate (child_allocation);
+
+ child_allocation.y += child_min;
+ }
+ }
+}
diff --git a/src/test-sorted.vala b/src/test-sorted.vala
new file mode 100644
index 0000000..a940439
--- /dev/null
+++ b/src/test-sorted.vala
@@ -0,0 +1,108 @@
+/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2011 Alexander Larsson <alexl redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using Gtk;
+using Contacts;
+
+
+public static int
+compare_label (Widget a, Widget b) {
+ var aa = (a as Label).get_text ();
+ var bb = (b as Label).get_text ();
+ return strcmp (aa, bb);
+}
+
+public static int
+compare_label_reverse (Widget a, Widget b) {
+ return - compare_label (a, b);
+}
+
+public static bool
+filter (Widget widget) {
+ var text = (widget as Label).get_text ();
+ return strcmp (text, "blah2") != 0;
+}
+
+public static int
+main (string[] args) {
+
+ Gtk.init (ref args);
+
+ var w = new Window ();
+ var hbox = new Box(Orientation.HORIZONTAL, 0);
+ w.add (hbox);
+
+ var sorted = new Sorted();
+ hbox.add (sorted);
+
+ sorted.add (new Label ("blah4"));
+ var l3 = new Label ("blah3");
+ sorted.add (l3);
+ sorted.add (new Label ("blah1"));
+ sorted.add (new Label ("blah2"));
+
+ var vbox = new Box(Orientation.VERTICAL, 0);
+ hbox.add (vbox);
+
+ var b = new Button.with_label ("sort");
+ vbox.add (b);
+ b.clicked.connect ( () => {
+ sorted.set_sort_func (compare_label);
+ });
+
+ b = new Button.with_label ("reverse");
+ vbox.add (b);
+ b.clicked.connect ( () => {
+ sorted.set_sort_func (compare_label_reverse);
+ });
+
+ b = new Button.with_label ("change");
+ vbox.add (b);
+ b.clicked.connect ( () => {
+ l3.set_label ("blah5");
+ sorted.child_changed (l3);
+ });
+
+ b = new Button.with_label ("filter");
+ vbox.add (b);
+ b.clicked.connect ( () => {
+ sorted.set_filter_func (filter);
+ });
+
+ b = new Button.with_label ("unfilter");
+ vbox.add (b);
+ b.clicked.connect ( () => {
+ sorted.set_filter_func (null);
+ });
+
+ int new_button_nr = 1;
+ b = new Button.with_label ("add");
+ vbox.add (b);
+ b.clicked.connect ( () => {
+ var l = new Label ("blah2 new %d".printf (new_button_nr++));
+ sorted.add (l);
+ l.show ();
+ });
+
+
+ w.show_all ();
+
+ Gtk.main ();
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]