[evolution] Bug #724059 - Allow arbitrary number of phone entries in the contact editor



commit 2263c3c6049be334e48c650e06332e18ca5af7d2
Author: Christian Schaarschmidt <schaarsc gmx de>
Date:   Mon Mar 24 16:29:31 2014 +0100

    Bug #724059 - Allow arbitrary number of phone entries in the contact editor

 addressbook/gui/contact-editor/Makefile.am         |    4 +-
 addressbook/gui/contact-editor/contact-editor.ui   |  220 +------
 .../gui/contact-editor/e-contact-editor-dyntable.c |  738 ++++++++++++++++++++
 .../gui/contact-editor/e-contact-editor-dyntable.h |  126 ++++
 addressbook/gui/contact-editor/e-contact-editor.c  |  372 ++++------
 5 files changed, 1026 insertions(+), 434 deletions(-)
---
diff --git a/addressbook/gui/contact-editor/Makefile.am b/addressbook/gui/contact-editor/Makefile.am
index 4e5d0b7..c8499b7 100644
--- a/addressbook/gui/contact-editor/Makefile.am
+++ b/addressbook/gui/contact-editor/Makefile.am
@@ -21,7 +21,9 @@ libecontacteditor_la_SOURCES =                        \
        e-contact-editor.c                      \
        e-contact-editor.h                      \
        e-contact-quick-add.c                   \
-       e-contact-quick-add.h
+       e-contact-quick-add.h \
+       e-contact-editor-dyntable.h \
+       e-contact-editor-dyntable.c
 
 libecontacteditor_la_LDFLAGS = -avoid-version $(NO_UNDEFINED)
 
diff --git a/addressbook/gui/contact-editor/contact-editor.ui 
b/addressbook/gui/contact-editor/contact-editor.ui
index 7e48bc5..9b7e94e 100644
--- a/addressbook/gui/contact-editor/contact-editor.ui
+++ b/addressbook/gui/contact-editor/contact-editor.ui
@@ -528,215 +528,17 @@
                                         <property name="border_width">12</property>
                                         <property name="orientation">vertical</property>
                                         <property name="spacing">6</property>
-                                        <child>
-                                          <object class="GtkTable" id="table84">
-                                            <property name="visible">True</property>
-                                            <property name="n_rows">2</property>
-                                            <property name="n_columns">4</property>
-                                            <property name="column_spacing">6</property>
-                                            <property name="row_spacing">6</property>
-                                            <child>
-                                              <object class="GtkEntry" id="entry-phone-1">
-                                                <property name="visible">True</property>
-                                                <property name="can_focus">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">1</property>
-                                                <property name="right_attach">2</property>
-                                                <property name="y_options"></property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkEntry" id="entry-phone-3">
-                                                <property name="visible">True</property>
-                                                <property name="can_focus">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">1</property>
-                                                <property name="right_attach">2</property>
-                                                <property name="top_attach">1</property>
-                                                <property name="bottom_attach">2</property>
-                                                <property name="y_options"></property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkEntry" id="entry-phone-4">
-                                                <property name="visible">True</property>
-                                                <property name="can_focus">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">3</property>
-                                                <property name="right_attach">4</property>
-                                                <property name="top_attach">1</property>
-                                                <property name="bottom_attach">2</property>
-                                                <property name="y_options"></property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkEntry" id="entry-phone-2">
-                                                <property name="visible">True</property>
-                                                <property name="can_focus">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">3</property>
-                                                <property name="right_attach">4</property>
-                                                <property name="y_options"></property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkComboBox" id="combobox-phone-1">
-                                                <property name="visible">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="x_options">GTK_FILL</property>
-                                                <property name="y_options">GTK_FILL</property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkComboBox" id="combobox-phone-3">
-                                                <property name="visible">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="top_attach">1</property>
-                                                <property name="bottom_attach">2</property>
-                                                <property name="x_options">GTK_FILL</property>
-                                                <property name="y_options">GTK_FILL</property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkComboBox" id="combobox-phone-2">
-                                                <property name="visible">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">2</property>
-                                                <property name="right_attach">3</property>
-                                                <property name="x_options">GTK_FILL</property>
-                                                <property name="y_options">GTK_FILL</property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkComboBox" id="combobox-phone-4">
-                                                <property name="visible">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">2</property>
-                                                <property name="right_attach">3</property>
-                                                <property name="top_attach">1</property>
-                                                <property name="bottom_attach">2</property>
-                                                <property name="x_options">GTK_FILL</property>
-                                                <property name="y_options">GTK_FILL</property>
-                                              </packing>
-                                            </child>
-                                          </object>
-                                          <packing>
-                                            <property name="expand">False</property>
-                                            <property name="position">0</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <object class="GtkTable" id="table-phone-extended">
-                                            <property name="n_rows">2</property>
-                                            <property name="n_columns">4</property>
-                                            <property name="column_spacing">6</property>
-                                            <property name="row_spacing">6</property>
-                                            <child>
-                                              <object class="GtkEntry" id="entry-phone-5">
-                                                <property name="visible">True</property>
-                                                <property name="can_focus">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">1</property>
-                                                <property name="right_attach">2</property>
-                                                <property name="y_options"></property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkEntry" id="entry-phone-7">
-                                                <property name="visible">True</property>
-                                                <property name="can_focus">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">1</property>
-                                                <property name="right_attach">2</property>
-                                                <property name="top_attach">1</property>
-                                                <property name="bottom_attach">2</property>
-                                                <property name="y_options"></property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkEntry" id="entry-phone-6">
-                                                <property name="visible">True</property>
-                                                <property name="can_focus">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">3</property>
-                                                <property name="right_attach">4</property>
-                                                <property name="y_options"></property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkEntry" id="entry-phone-8">
-                                                <property name="visible">True</property>
-                                                <property name="can_focus">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">3</property>
-                                                <property name="right_attach">4</property>
-                                                <property name="top_attach">1</property>
-                                                <property name="bottom_attach">2</property>
-                                                <property name="y_options"></property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkComboBox" id="combobox-phone-5">
-                                                <property name="visible">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="x_options">GTK_FILL</property>
-                                                <property name="y_options">GTK_FILL</property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkComboBox" id="combobox-phone-7">
-                                                <property name="visible">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="top_attach">1</property>
-                                                <property name="bottom_attach">2</property>
-                                                <property name="x_options">GTK_FILL</property>
-                                                <property name="y_options">GTK_FILL</property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkComboBox" id="combobox-phone-6">
-                                                <property name="visible">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">2</property>
-                                                <property name="right_attach">3</property>
-                                                <property name="x_options">GTK_FILL</property>
-                                                <property name="y_options">GTK_FILL</property>
-                                              </packing>
-                                            </child>
-                                            <child>
-                                              <object class="GtkComboBox" id="combobox-phone-8">
-                                                <property name="visible">True</property>
-                                              </object>
-                                              <packing>
-                                                <property name="left_attach">2</property>
-                                                <property name="right_attach">3</property>
-                                                <property name="top_attach">1</property>
-                                                <property name="bottom_attach">2</property>
-                                                <property name="x_options">GTK_FILL</property>
-                                                <property name="y_options">GTK_FILL</property>
-                                              </packing>
-                                            </child>
-                                          </object>
-                                          <packing>
-                                            <property name="expand">False</property>
-                                            <property name="position">1</property>
-                                          </packing>
-                                        </child>
+                                                  
+                                           <child>
+                                             <object class="EContactEditorDynTable" 
type-func="e_contact_editor_dyntable_get_type" id="phone-dyntable">
+                                               <property name="visible">True</property>
+                                             </object>
+                                               <packing>
+                                                   <property name="position">0</property>
+                                               </packing>
+                                           </child>
+
+   
                                       </object>
                                     </child>
                                     <child type="label">
diff --git a/addressbook/gui/contact-editor/e-contact-editor-dyntable.c 
b/addressbook/gui/contact-editor/e-contact-editor-dyntable.c
new file mode 100644
index 0000000..4706d5b
--- /dev/null
+++ b/addressbook/gui/contact-editor/e-contact-editor-dyntable.c
@@ -0,0 +1,738 @@
+/*
+ * e-contact-editor-dyntable.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "e-contact-editor-dyntable.h"
+
+G_DEFINE_TYPE(EContactEditorDynTable, e_contact_editor_dyntable, GTK_TYPE_GRID)
+
+/* one position is occupied by two widgets: combo+entry */
+#define ENTRY_SIZE 2
+#define MAX_CAPACITY 100
+
+enum {
+       CHANGED_SIGNAL,
+       ACTIVATE_SIGNAL,
+       ROW_ADDED_SIGNAL,
+       LAST_SIGNAL
+};
+
+static guint dyntable_signals[LAST_SIGNAL];
+
+struct _EContactEditorDynTablePrivate {
+
+       /* absolute max, dyntable will ignore the rest */
+       guint   max_entries;
+
+       /* current number of entries with text or requested by user*/
+       guint   curr_entries;
+
+       /* minimum to show, with or without text */
+       guint   show_min_entries;
+
+       /* no matter how much data, show only */
+       guint   show_max_entries;
+
+       /* number of entries (combo/text) per row */
+       guint   columns;
+
+       /* if true, fill line with empty slots*/
+       gboolean justified;
+
+       GtkWidget       *add_button;
+       GtkListStore    *combo_store;
+       GtkListStore    *data_store;
+
+       /* array of default values for combo box */
+       const gint      *combo_defaults;
+
+       /* number of elements in the array */
+       size_t          combo_defaults_n;
+};
+
+GtkWidget*
+e_contact_editor_dyntable_new (void)
+{
+       GtkWidget* widget;
+       widget = GTK_WIDGET (g_object_new (e_contact_editor_dyntable_get_type (), NULL));
+       return widget;
+}
+
+static void
+set_combo_box_active (EContactEditorDynTable *dyntable,
+                      GtkComboBox *combo_box,
+                      gint active)
+{
+       g_signal_handlers_block_matched (combo_box,
+                       G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+                       NULL, dyntable);
+       gtk_combo_box_set_active (combo_box, active);
+       g_signal_handlers_unblock_matched (combo_box,
+                                          G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+                                          NULL, dyntable);
+}
+
+/* translate position into column/row */
+static void
+position_to_grid (EContactEditorDynTable *dyntable,
+                  guint pos,
+                  guint *col,
+                  guint *row)
+{
+       *row = pos / dyntable->priv->columns;
+       *col = pos % dyntable->priv->columns * ENTRY_SIZE;
+}
+
+static void
+move_widget (GtkGrid *grid, GtkWidget *w, guint col, guint row)
+{
+       GValue rowValue = G_VALUE_INIT, colValue = G_VALUE_INIT;
+
+       g_value_init (&rowValue, G_TYPE_UINT);
+       g_value_init (&colValue, G_TYPE_UINT);
+
+       g_value_set_uint (&rowValue, row);
+       g_value_set_uint (&colValue, col);
+
+       gtk_container_child_set_property (GTK_CONTAINER (grid), w,
+                                         "left-attach", &colValue);
+       gtk_container_child_set_property (GTK_CONTAINER (grid), w,
+                                         "top-attach", &rowValue);
+}
+
+static gboolean
+is_button_required (EContactEditorDynTable *dyntable)
+{
+       if (dyntable->priv->curr_entries >= dyntable->priv->max_entries)
+               return FALSE;
+       if (dyntable->priv->curr_entries <= dyntable->priv->show_max_entries)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+static void
+sensitize_button (EContactEditorDynTable *dyntable)
+{
+       guint row, col, current_entries;
+       GtkWidget *w;
+       GtkGrid *grid;
+       EContactEditorDynTableClass *class;
+       gboolean enabled;
+
+       grid = GTK_GRID (dyntable);
+       class = E_CONTACT_EDITOR_DYNTABLE_GET_CLASS (dyntable);
+
+       current_entries = dyntable->priv->curr_entries;
+       enabled = TRUE;
+       if (current_entries > 0) {
+               /* last entry */
+               current_entries--;
+               position_to_grid (dyntable, current_entries, &col, &row);
+               w = gtk_grid_get_child_at (grid, col + 1, row);
+
+               enabled = !class->widget_is_empty (dyntable, w);
+       }
+
+       gtk_widget_set_sensitive (dyntable->priv->add_button, enabled);
+}
+
+static void
+show_button (EContactEditorDynTable *dyntable)
+{
+       guint col,row, pos;
+       gboolean visible = FALSE;
+       GtkGrid *grid;
+
+       grid = GTK_GRID (dyntable);
+
+       /* move button to end of current line */
+       pos = dyntable->priv->curr_entries;
+       if (pos > 0)
+               pos--;
+       position_to_grid(dyntable, pos, &col, &row);
+       move_widget (grid, dyntable->priv->add_button,
+                    dyntable->priv->columns*ENTRY_SIZE+1, row);
+
+       /* set visibility */
+       if (is_button_required (dyntable))
+               visible = TRUE;
+
+       gtk_widget_set_visible (dyntable->priv->add_button, visible);
+
+       sensitize_button (dyntable);
+}
+
+static void
+increment_counter (EContactEditorDynTable *dyntable)
+{
+       dyntable->priv->curr_entries++;
+       show_button (dyntable);
+}
+
+static void
+decrement_counter (EContactEditorDynTable *dyntable)
+{
+       dyntable->priv->curr_entries--;
+       show_button (dyntable);
+}
+
+static gint
+get_next_combo_index (EContactEditorDynTable *dyntable)
+{
+       size_t array_size = dyntable->priv->combo_defaults_n;
+       gint idx = 0;
+
+       if (dyntable->priv->combo_defaults != NULL) {
+               idx = dyntable->priv->combo_defaults[dyntable->priv->curr_entries % array_size];
+       }
+
+       return idx;
+}
+
+static GtkWidget*
+combo_box_create (EContactEditorDynTable *dyntable)
+{
+       GtkWidget *w;
+       GtkComboBox *combo;
+       GtkListStore *store;
+       GtkCellRenderer *cell;
+
+       w = gtk_combo_box_new ();
+       combo = GTK_COMBO_BOX (w);
+       store = dyntable->priv->combo_store;
+
+       gtk_combo_box_set_model (combo, GTK_TREE_MODEL(store));
+
+       gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
+
+       cell = gtk_cell_renderer_text_new ();
+       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
+       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
+                                       "text", DYNTABLE_COMBO_COLUMN_TEXT,
+                                       "sensitive", DYNTABLE_COMBO_COLUMN_SENSITIVE,
+                                       NULL);
+
+       gtk_combo_box_set_active (combo, get_next_combo_index (dyntable));
+
+       return w;
+}
+
+static void
+emit_changed (EContactEditorDynTable *dyntable)
+{
+       g_signal_emit (dyntable, dyntable_signals[CHANGED_SIGNAL], 0);
+}
+
+static void
+emit_activated (EContactEditorDynTable *dyntable)
+{
+       g_signal_emit (dyntable, dyntable_signals[ACTIVATE_SIGNAL], 0);
+}
+
+static void
+emit_row_added (EContactEditorDynTable *dyntable)
+{
+       g_signal_emit (dyntable, dyntable_signals[ROW_ADDED_SIGNAL], 0);
+}
+
+static void
+add_empty_entry (EContactEditorDynTable *dyntable)
+{
+       GtkGrid *grid;
+       guint row, col;
+       GtkWidget *box, *entry;
+       EContactEditorDynTableClass *class;
+
+       grid = GTK_GRID (dyntable);
+       position_to_grid (dyntable, dyntable->priv->curr_entries, &col, &row);
+
+       /* create new entry at last position */
+       class = E_CONTACT_EDITOR_DYNTABLE_GET_CLASS (dyntable);
+       box = combo_box_create (dyntable);
+       gtk_grid_attach (grid, box, col, row, 1, 1);
+       gtk_widget_show (box);
+
+       entry = class->widget_create (dyntable);
+       g_object_set (entry, "margin-left", 2, NULL);
+       g_object_set (entry, "margin-right", 5, NULL);
+       gtk_widget_set_hexpand (entry, GTK_EXPAND);
+       gtk_grid_attach (grid, entry, col + 1, row, 1, 1);
+       gtk_widget_show (entry);
+
+       g_signal_connect_swapped(box, "changed",
+                                G_CALLBACK (gtk_widget_grab_focus), entry);
+       g_signal_connect_swapped(box, "changed",
+                                G_CALLBACK (emit_changed), dyntable);
+       g_signal_connect_swapped(entry, "changed",
+                                G_CALLBACK (emit_changed), dyntable);
+       g_signal_connect_swapped(entry, "changed",
+                                G_CALLBACK (sensitize_button), dyntable);
+       g_signal_connect_swapped(entry, "activate",
+                                G_CALLBACK (emit_activated), dyntable);
+
+       increment_counter (dyntable);
+
+       if ( (dyntable->priv->justified && col < dyntable->priv->columns-1) ||
+            (dyntable->priv->curr_entries < dyntable->priv->show_min_entries) )
+               add_empty_entry (dyntable);
+
+       gtk_widget_grab_focus (entry);
+}
+
+static void
+remove_empty_entries (EContactEditorDynTable *dyntable, gboolean fillup)
+{
+       guint row, col, pos;
+       GtkGrid* grid;
+       GtkWidget* w;
+       EContactEditorDynTableClass *class;
+
+       grid = GTK_GRID (dyntable);
+       class = E_CONTACT_EDITOR_DYNTABLE_GET_CLASS (dyntable);
+
+       for (pos = 0; pos < dyntable->priv->curr_entries; pos++) {
+               position_to_grid (dyntable, pos, &col, &row);
+               w = gtk_grid_get_child_at (grid, col + 1, row);
+
+               if (w != NULL && class->widget_is_empty (dyntable, w)) {
+                       guint pos2, next_col, next_row;
+
+                       gtk_widget_destroy (w);
+                       w = gtk_grid_get_child_at (grid, col, row);
+                       gtk_widget_destroy (w);
+
+                       /* now fill gap */
+                       for (pos2 = pos + 1; pos2 < dyntable->priv->curr_entries; pos2++) {
+                               position_to_grid (dyntable, pos2, &next_col, &next_row);
+                               w = gtk_grid_get_child_at (grid, next_col, next_row);
+                               move_widget (grid, w, col, row);
+                               w = gtk_grid_get_child_at (grid, next_col + 1, next_row);
+                               move_widget (grid, w, col + 1, row);
+                               col = next_col;
+                               row = next_row;
+                       }
+                       decrement_counter (dyntable);
+                       pos--; /* check the new widget on our current position */
+               }
+
+       }
+
+       if (fillup && dyntable->priv->curr_entries < dyntable->priv->show_min_entries)
+               add_empty_entry (dyntable);
+
+}
+
+/* clears data, not the combo box list store */
+void
+e_contact_editor_dyntable_clear_data (EContactEditorDynTable *dyntable)
+{
+       guint i, col, row;
+       GtkGrid *grid;
+       GtkWidget *w;
+       EContactEditorDynTableClass *class;
+
+       grid = GTK_GRID(dyntable);
+       class = E_CONTACT_EDITOR_DYNTABLE_GET_CLASS(dyntable);
+
+       for (i = 0; i < dyntable->priv->curr_entries; i++) {
+               position_to_grid (dyntable, i, &col, &row);
+               w = gtk_grid_get_child_at (grid, col + 1, row);
+               class->widget_clear (dyntable, w);
+       }
+       remove_empty_entries (dyntable, TRUE);
+
+       gtk_list_store_clear (dyntable->priv->data_store);
+}
+
+static void
+adjust_visibility_of_widgets (EContactEditorDynTable *dyntable)
+{
+       guint pos, col, row;
+       GtkGrid *grid;
+       GtkWidget *w;
+
+       grid = GTK_GRID (dyntable);
+       for (pos = 0; pos < dyntable->priv->curr_entries; pos++) {
+               gboolean visible = FALSE;
+
+               if (pos < dyntable->priv->show_max_entries)
+                       visible = TRUE;
+
+               position_to_grid (dyntable, pos, &col, &row);
+               w = gtk_grid_get_child_at (grid, col, row);
+               gtk_widget_set_visible (w, visible);
+               w = gtk_grid_get_child_at (grid, col + 1, row);
+               gtk_widget_set_visible (w, visible);
+       }
+
+       show_button (dyntable);
+}
+
+/* number of columns can only be set before any data is added to this dyntable */
+void e_contact_editor_dyntable_set_num_columns (EContactEditorDynTable *dyntable,
+                                                guint number_of_columns,
+                                                gboolean justified)
+{
+       GtkTreeIter iter;
+       GtkTreeModel *store;
+       gboolean holds_data;
+
+       g_return_if_fail (number_of_columns > 0);
+
+       store = GTK_TREE_MODEL (dyntable->priv->data_store);
+       holds_data = gtk_tree_model_get_iter_first (store, &iter);
+       g_return_if_fail (!holds_data);
+
+       remove_empty_entries(dyntable, FALSE);
+
+       dyntable->priv->columns = number_of_columns;
+       dyntable->priv->justified = justified;
+
+       remove_empty_entries(dyntable, TRUE);
+}
+
+void
+e_contact_editor_dyntable_set_max_entries (EContactEditorDynTable *dyntable,
+                                           guint max)
+{
+       GtkTreeIter iter;
+       GtkTreeModel *store;
+       gboolean holds_data;
+
+       g_return_if_fail (max > 0);
+
+       store = GTK_TREE_MODEL (dyntable->priv->data_store);
+       holds_data = gtk_tree_model_get_iter_first (store, &iter);
+       g_return_if_fail (!holds_data);
+
+       dyntable->priv->max_entries = max;
+       if (dyntable->priv->show_max_entries>max)
+               dyntable->priv->show_max_entries = max;
+       if (dyntable->priv->show_min_entries>max)
+                       dyntable->priv->show_min_entries = max;
+}
+
+/* show at least number_of_entries, with or without data */
+void
+e_contact_editor_dyntable_set_show_min (EContactEditorDynTable *dyntable,
+                                        guint number_of_entries)
+{
+       if (number_of_entries > dyntable->priv->show_max_entries)
+               dyntable->priv->show_min_entries = dyntable->priv->show_max_entries;
+       else
+               dyntable->priv->show_min_entries = number_of_entries;
+
+       if (dyntable->priv->curr_entries < dyntable->priv->show_min_entries)
+               add_empty_entry (dyntable);
+
+       adjust_visibility_of_widgets (dyntable);
+}
+
+/* show no more than number_of_entries, hide the rest */
+void
+e_contact_editor_dyntable_set_show_max (EContactEditorDynTable *dyntable,
+                                        guint number_of_entries)
+{
+       if (number_of_entries > dyntable->priv->max_entries) {
+               dyntable->priv->show_max_entries = dyntable->priv->max_entries;
+       } else if (number_of_entries < dyntable->priv->show_min_entries) {
+               dyntable->priv->show_max_entries = dyntable->priv->show_min_entries;
+       } else {
+               dyntable->priv->show_max_entries = number_of_entries;
+       }
+
+       adjust_visibility_of_widgets (dyntable);
+}
+
+/* use data_store to fill data into widgets */
+void
+e_contact_editor_dyntable_fill_in_data (EContactEditorDynTable *dyntable)
+{
+       guint pos = 0, col, row;
+       EContactEditorDynTableClass *class;
+       GtkGrid *grid;
+       GtkTreeIter iter;
+       GtkTreeModel *store;
+       GtkWidget *w;
+       gboolean valid;
+       gchar *str_data;
+       gint int_data;
+
+       class = E_CONTACT_EDITOR_DYNTABLE_GET_CLASS (dyntable);
+       grid = GTK_GRID (dyntable);
+       store = GTK_TREE_MODEL (dyntable->priv->data_store);
+
+       valid = gtk_tree_model_get_iter_first (store, &iter);
+       while (valid) {
+               gtk_tree_model_get (store, &iter,
+                               DYNTABLE_STORE_COLUMN_ENTRY_STRING, &str_data,
+                               DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &int_data,
+                               -1);
+
+               if (pos >= dyntable->priv->curr_entries)
+                       add_empty_entry (dyntable);
+
+               position_to_grid (dyntable, pos++, &col, &row);
+               w = gtk_grid_get_child_at (grid, col, row);
+               set_combo_box_active (dyntable, GTK_COMBO_BOX(w), int_data);
+               w = gtk_grid_get_child_at (grid, col + 1, row);
+               class->widget_fill (dyntable, w, str_data);
+
+               g_return_if_fail(pos < dyntable->priv->max_entries);
+               valid = gtk_tree_model_iter_next (store, &iter);
+       }
+
+       /* fix visibility of added items */
+       adjust_visibility_of_widgets (dyntable);
+}
+
+/* the model returned has 3 columns
+ *
+ * UINT: sort order
+ * UINT: active combo item
+ * STRING: data extracted with widget_extract()
+ */
+GtkListStore*
+e_contact_editor_dyntable_extract_data (EContactEditorDynTable *dyntable)
+{
+       EContactEditorDynTableClass *class;
+       GtkGrid *grid;
+       GtkListStore *data_store;
+       GtkWidget *w;
+       guint pos, col, row;
+
+       grid = GTK_GRID(dyntable);
+       class = E_CONTACT_EDITOR_DYNTABLE_GET_CLASS(dyntable);
+       data_store = dyntable->priv->data_store;
+
+       gtk_list_store_clear (data_store);
+
+       for (pos = 0; pos < dyntable->priv->curr_entries; pos++) {
+
+               position_to_grid (dyntable, pos, &col, &row);
+               w = gtk_grid_get_child_at (grid, col + 1, row);
+
+               if (!class->widget_is_empty (dyntable, w)) {
+                       GtkTreeIter iter;
+                       gchar *dup;
+                       gint combo_item;
+                       const gchar *data;
+
+                       data = class->widget_extract (dyntable, w);
+                       w = gtk_grid_get_child_at (grid, col, row);
+                       combo_item = gtk_combo_box_get_active (GTK_COMBO_BOX(w));
+
+                       dup = g_strdup (data);
+                       g_strstrip(dup);
+
+                       gtk_list_store_append (data_store, &iter);
+                       gtk_list_store_set (data_store, &iter,
+                                           DYNTABLE_STORE_COLUMN_SORTORDER, pos,
+                                           DYNTABLE_STORE_COLUMN_SELECTED_ITEM, combo_item,
+                                           DYNTABLE_STORE_COLUMN_ENTRY_STRING, dup,
+                                           -1);
+
+                       g_free (dup);
+               }
+       }
+
+       return dyntable->priv->data_store;
+}
+
+/* the model returned has two columns
+ *
+ * STRING: bound to attribute "text"
+ * BOOLEAN: bound to attribute "sensitive"
+ */
+GtkListStore*
+e_contact_editor_dyntable_get_combo_store (EContactEditorDynTable *dyntable)
+{
+       return dyntable->priv->combo_store;
+}
+
+void
+e_contact_editor_dyntable_set_combo_defaults (EContactEditorDynTable *dyntable,
+                                              const gint *defaults,
+                                              size_t defaults_n)
+{
+       dyntable->priv->combo_defaults = defaults;
+       dyntable->priv->combo_defaults_n = defaults_n;
+}
+
+static void
+dispose_impl (GObject *object)
+{
+       GtkListStore *store;
+       EContactEditorDynTable *dyntable;
+
+       dyntable = E_CONTACT_EDITOR_DYNTABLE(object);
+
+       store = dyntable->priv->data_store;
+       if (store) {
+               gtk_list_store_clear (store);
+               g_object_unref (store);
+               dyntable->priv->data_store = NULL;
+       }
+
+       store = dyntable->priv->combo_store;
+       if (store) {
+               g_object_unref (store);
+               dyntable->priv->combo_store = NULL;
+       }
+
+       G_OBJECT_CLASS(e_contact_editor_dyntable_parent_class)->dispose (object);
+}
+
+static GtkWidget*
+default_impl_widget_create (EContactEditorDynTable *dyntable)
+{
+       return gtk_entry_new ();
+}
+
+static void
+default_impl_widget_clear (EContactEditorDynTable *dyntable,
+                           GtkWidget *w)
+{
+       GtkEntry *e;
+       e = GTK_ENTRY(w);
+       gtk_entry_set_text (e, "");
+}
+
+static const gchar*
+default_impl_widget_extract (EContactEditorDynTable *dyntable,
+                             GtkWidget *w)
+{
+       GtkEntry *e;
+
+       e = GTK_ENTRY(w);
+       return gtk_entry_get_text (e);
+}
+
+static void
+default_impl_widget_fill (EContactEditorDynTable *dyntable,
+                          GtkWidget *w,
+                          const gchar *value)
+{
+       GtkEntry *e;
+
+       e = GTK_ENTRY(w);
+       gtk_entry_set_text (e, value);
+}
+
+static gboolean
+default_impl_widget_is_empty (EContactEditorDynTable *dyntable,
+                              GtkWidget *w)
+{
+       GtkEntry *e;
+       const gchar *data;
+       gchar * dup;
+       size_t len = -1;
+
+       e = GTK_ENTRY(w);
+       if (0 == gtk_entry_get_text_length (e))
+               return TRUE;
+
+       data = gtk_entry_get_text (e);
+
+       dup = g_strdup (data);
+       g_strchug (dup);
+       len = strlen (dup);
+       g_free (dup);
+
+       return len <= 0;
+}
+
+static void
+e_contact_editor_dyntable_init (EContactEditorDynTable *dyntable)
+{
+       GtkGrid *grid;
+
+       dyntable->priv = G_TYPE_INSTANCE_GET_PRIVATE(dyntable,
+                       E_TYPE_CONTACT_EDITOR_DYNTABLE,
+                       EContactEditorDynTablePrivate);
+
+       /* fill in defaults */
+       dyntable->priv->max_entries = MAX_CAPACITY;
+       dyntable->priv->curr_entries = 0;
+       dyntable->priv->show_min_entries = 0;
+       dyntable->priv->show_max_entries = dyntable->priv->max_entries;
+       dyntable->priv->columns = 2;
+       dyntable->priv->justified = FALSE;
+       dyntable->priv->combo_defaults = NULL;
+
+       dyntable->priv->combo_store = gtk_list_store_new (DYNTABLE_COBMO_COLUMN_NUM_COLUMNS,
+                       G_TYPE_STRING, G_TYPE_BOOLEAN);
+       dyntable->priv->data_store = gtk_list_store_new (DYNTABLE_STORE_COLUMN_NUM_COLUMNS,
+                       G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING);
+       gtk_tree_sortable_set_sort_column_id (
+                       GTK_TREE_SORTABLE (dyntable->priv->data_store),
+                       DYNTABLE_STORE_COLUMN_SORTORDER,
+                       GTK_SORT_ASCENDING);
+
+       dyntable->priv->add_button = gtk_button_new_with_label ("+");
+       g_signal_connect_swapped (dyntable->priv->add_button, "clicked",
+                       G_CALLBACK (add_empty_entry), dyntable);
+       g_signal_connect_swapped(dyntable->priv->add_button, "clicked",
+                       G_CALLBACK (emit_row_added), dyntable);
+
+       grid = GTK_GRID (dyntable);
+
+       gtk_grid_attach (grid, dyntable->priv->add_button, 0, 0, 1, 1);
+       gtk_widget_set_valign (dyntable->priv->add_button, GTK_ALIGN_CENTER);
+       gtk_widget_set_halign (dyntable->priv->add_button, GTK_ALIGN_START);
+       gtk_widget_show (dyntable->priv->add_button);
+
+       if (dyntable->priv->curr_entries < dyntable->priv->show_min_entries)
+               add_empty_entry (dyntable);
+}
+
+static void
+e_contact_editor_dyntable_class_init (EContactEditorDynTableClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof(EContactEditorDynTablePrivate));
+
+       dyntable_signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+                       G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                       G_STRUCT_OFFSET(EContactEditorDynTableClass, changed),
+                       NULL, NULL,
+                       g_cclosure_marshal_VOID__VOID,
+                       G_TYPE_NONE, 0);
+       dyntable_signals[ACTIVATE_SIGNAL] = g_signal_new ("activate",
+                       G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                       G_STRUCT_OFFSET(EContactEditorDynTableClass, activate),
+                       NULL, NULL,
+                       g_cclosure_marshal_VOID__VOID,
+                       G_TYPE_NONE, 0);
+       dyntable_signals[ROW_ADDED_SIGNAL] = g_signal_new ("row-added",
+                       G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                       G_STRUCT_OFFSET(EContactEditorDynTableClass, row_added),
+                       NULL, NULL,
+                       g_cclosure_marshal_VOID__VOID,
+                       G_TYPE_NONE, 0);
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = dispose_impl;
+
+       /* virtual functions */
+       class->widget_create    = default_impl_widget_create;
+       class->widget_is_empty  = default_impl_widget_is_empty;
+       class->widget_clear     = default_impl_widget_clear;
+       class->widget_extract   = default_impl_widget_extract;
+       class->widget_fill      = default_impl_widget_fill;
+}
diff --git a/addressbook/gui/contact-editor/e-contact-editor-dyntable.h 
b/addressbook/gui/contact-editor/e-contact-editor-dyntable.h
new file mode 100644
index 0000000..c5c9238
--- /dev/null
+++ b/addressbook/gui/contact-editor/e-contact-editor-dyntable.h
@@ -0,0 +1,126 @@
+/*
+ * e-contact-editor-dyntable.h
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_CONTACT_EDITOR_DYNTABLE_H_
+#define E_CONTACT_EDITOR_DYNTABLE_H_
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define E_TYPE_CONTACT_EDITOR_DYNTABLE \
+       (e_contact_editor_dyntable_get_type ())
+#define E_CONTACT_EDITOR_DYNTABLE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_CONTACT_EDITOR_DYNTABLE, EContactEditorDynTable))
+#define E_CONTACT_EDITOR_DYNTABLE_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((klass), E_TYPE_CONTACT_EDITOR_DYNTABLE, EContactEditorDynTableClass))
+#define E_IS_CONTACT_EDITOR_DYNTABLE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_CONTACT_EDITOR_DYNTABLE))
+#define E_IS_CONTACT_EDITOR_DYNTABLE_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((obj), E_TYPE_CONTACT_EDITOR_DYNTABLE))
+#define E_CONTACT_EDITOR_DYNTABLE_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       (obj,E_TYPE_CONTACT_EDITOR_DYNTABLE, EContactEditorDynTableClass))
+
+G_BEGIN_DECLS
+
+typedef enum {
+       DYNTABLE_STORE_COLUMN_SORTORDER,
+       DYNTABLE_STORE_COLUMN_SELECTED_ITEM,
+       DYNTABLE_STORE_COLUMN_ENTRY_STRING,
+       DYNTABLE_STORE_COLUMN_NUM_COLUMNS
+} EContactEditorDynTableStoreColumns;
+
+typedef enum {
+       DYNTABLE_COMBO_COLUMN_TEXT,
+       DYNTABLE_COMBO_COLUMN_SENSITIVE,
+       DYNTABLE_COBMO_COLUMN_NUM_COLUMNS
+} EContactEditorDynTableComboColumns;
+
+typedef struct _EContactEditorDynTable EContactEditorDynTable;
+typedef struct _EContactEditorDynTableClass EContactEditorDynTableClass;
+typedef struct _EContactEditorDynTablePrivate EContactEditorDynTablePrivate;
+
+struct _EContactEditorDynTableClass {
+       GtkGridClass parent_class;
+
+       /* Signals */
+       void            (*changed)              (EContactEditorDynTable* dyntable);
+       void            (*activate)             (EContactEditorDynTable* dyntable);
+       void            (*row_added)            (EContactEditorDynTable* dyntable);
+
+       /* virtual */
+
+       /* defaults to GtkEntiy */
+       GtkWidget*      (*widget_create)        (EContactEditorDynTable *dyntable);
+
+       /* defaults to string_is_empty(txt) */
+       gboolean        (*widget_is_empty)      (EContactEditorDynTable *dyntable,
+                                                GtkWidget *w);
+
+       /* defaults to entry_set_text("") */
+       void            (*widget_clear)         (EContactEditorDynTable *dyntable,
+                                                GtkWidget *w);
+
+       /* default impl gtk_entry_set_text
+        * other widgets may need to "parse" value before usage.
+        */
+       void            (*widget_fill)          (EContactEditorDynTable *dyntable,
+                                                GtkWidget *w,
+                                                const gchar *value);
+
+       /* default impl returns gtk_entry_get_text
+        * other widget may require some kind of "encoding"
+        */
+       const gchar*    (*widget_extract)       (EContactEditorDynTable *dyntable,
+                                                GtkWidget *w);
+
+};
+
+struct _EContactEditorDynTable {
+       GtkGrid parent;
+
+       EContactEditorDynTablePrivate* priv;
+};
+
+GtkWidget*     e_contact_editor_dyntable_new           (void);
+GType          e_contact_editor_dyntable_get_type      (void) G_GNUC_CONST;
+void           e_contact_editor_dyntable_set_show_min  (EContactEditorDynTable *dyntable,
+                                                        guint number_of_entries);
+void           e_contact_editor_dyntable_set_show_max  (EContactEditorDynTable *dyntable,
+                                                        guint number_of_entries);
+void           e_contact_editor_dyntable_set_num_columns (EContactEditorDynTable *dyntable,
+                                                          guint number_of_columns,
+                                                          gboolean justified);
+void           e_contact_editor_dyntable_set_max_entries (EContactEditorDynTable *dyntable,
+                                                          guint max);
+
+GtkListStore*  e_contact_editor_dyntable_get_combo_store (EContactEditorDynTable* dyntable);
+void           e_contact_editor_dyntable_set_combo_defaults (EContactEditorDynTable* dyntable,
+                                                             const gint *defaults,
+                                                             size_t defaults_n);
+void           e_contact_editor_dyntable_clear_data    (EContactEditorDynTable *dyntable);
+void           e_contact_editor_dyntable_fill_in_data  (EContactEditorDynTable *dyntable);
+GtkListStore*  e_contact_editor_dyntable_extract_data  (EContactEditorDynTable *dyntable);
+
+G_END_DECLS
+
+#endif /* E_CONTACT_EDITOR_DYNTABLE_H_ */
diff --git a/addressbook/gui/contact-editor/e-contact-editor.c 
b/addressbook/gui/contact-editor/e-contact-editor.c
index aa02ab0..a27f8cd 100644
--- a/addressbook/gui/contact-editor/e-contact-editor.c
+++ b/addressbook/gui/contact-editor/e-contact-editor.c
@@ -44,9 +44,12 @@
 #include "eab-contact-merging.h"
 
 #include "e-contact-editor-fullname.h"
+#include "e-contact-editor-dyntable.h"
 
+#define SLOTS_PER_LINE 2
+#define SLOTS_IN_COLLAPSED_STATE SLOTS_PER_LINE
 #define EMAIL_SLOTS   4
-#define PHONE_SLOTS   8
+#define PHONE_SLOTS   50
 #define IM_SLOTS      4
 #define ADDRESS_SLOTS 3
 
@@ -1316,52 +1319,6 @@ init_item_sensitiveable_combo_box (GtkComboBox *combo)
                "text", 0, "sensitive", 1, NULL);
 }
 
-/* EContact can get attributes by field ID only,
- * and there is none for TEL, so we need this */
-static GList *
-get_attributes_named (EVCard *vcard,
-                      const gchar *attr_name)
-{
-       GList *attr_list_in;
-       GList *attr_list_out = NULL;
-       GList *l;
-
-       attr_list_in = e_vcard_get_attributes (vcard);
-
-       for (l = attr_list_in; l; l = g_list_next (l)) {
-               EVCardAttribute *attr = l->data;
-               const gchar *name;
-
-               name = e_vcard_attribute_get_name (attr);
-
-               if (!g_ascii_strcasecmp (attr_name, name)) {
-                       attr_list_out = g_list_append (
-                               attr_list_out,
-                               e_vcard_attribute_copy (attr));
-               }
-       }
-
-       return attr_list_out;
-}
-
-/* EContact can set attributes by field ID only,
- * and there is none for TEL, so we need this */
-static void
-set_attributes_named (EVCard *vcard,
-                      const gchar *attr_name,
-                      GList *attr_list)
-{
-       GList *l;
-
-       e_vcard_remove_attributes (vcard, NULL, attr_name);
-
-       for (l = attr_list; l; l = g_list_next (l)) {
-               EVCardAttribute *attr = l->data;
-
-               e_vcard_add_attribute (vcard, e_vcard_attribute_copy (attr));
-       }
-}
-
 static void
 set_arrow_image (EContactEditor *editor,
                  const gchar *arrow_widget,
@@ -1378,6 +1335,22 @@ set_arrow_image (EContactEditor *editor,
                        GTK_ARROW (arrow), GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
 }
 
+static gboolean
+is_arrow_image_arrow_down (EContactEditor *editor,
+                           const gchar *arrow_widget)
+{
+       GtkWidget *arrow;
+       gint value;
+
+       arrow  = e_builder_get_widget (editor->priv->builder, arrow_widget);
+       g_object_get (arrow, "arrow-type", &value, NULL);
+
+       if (value == GTK_ARROW_DOWN)
+               return TRUE;
+
+       return FALSE;
+}
+
 static void
 expand_widget_list (EContactEditor *editor,
                     const gchar **widget_names,
@@ -1407,13 +1380,18 @@ static void
 expand_phone (EContactEditor *editor,
               gboolean expanded)
 {
-       const gchar *names[] = {
-               "entry-phone-3", "combobox-phone-3",
-               "entry-phone-4", "combobox-phone-4",
-               "table-phone-extended", NULL
-       };
+       GtkWidget *w;
+       EContactEditorDynTable *dyntable;
+
        set_arrow_image (editor, "arrow-phone-expand", expanded);
-       expand_widget_list (editor, names, expanded);
+
+       w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
+       dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
+
+       if (expanded)
+               e_contact_editor_dyntable_set_show_max (dyntable, PHONE_SLOTS);
+       else
+               e_contact_editor_dyntable_set_show_max (dyntable, SLOTS_IN_COLLAPSED_STATE);
 }
 
 static void
@@ -1460,89 +1438,51 @@ init_email (EContactEditor *editor)
 }
 
 static void
-fill_in_phone_record (EContactEditor *editor,
-                      gint record,
-                      const gchar *phone,
-                      gint phone_type)
-{
-       GtkWidget *phone_type_combo_box;
-       GtkWidget *phone_entry;
-       gchar     *widget_name;
-
-       widget_name = g_strdup_printf ("combobox-phone-%d", record);
-       phone_type_combo_box = e_builder_get_widget (editor->priv->builder, widget_name);
-       g_free (widget_name);
-
-       widget_name = g_strdup_printf ("entry-phone-%d", record);
-       phone_entry = e_builder_get_widget (editor->priv->builder, widget_name);
-       g_free (widget_name);
-
-       set_combo_box_active (
-               editor, GTK_COMBO_BOX (phone_type_combo_box),
-               phone_type >= 0 ? phone_type : phones_default[record - 1]);
-       set_entry_text (editor, GTK_ENTRY (phone_entry), phone ? phone : "");
-
-       if (phone && *phone && record >= 3)
-               expand_phone (editor, TRUE);
-}
-
-static void
-extract_phone_record (EContactEditor *editor,
-                      gint record,
-                      gchar **phone,
-                      gint *phone_type)
-{
-       GtkWidget *phone_type_combo_box;
-       GtkWidget *phone_entry;
-       gchar     *widget_name;
-
-       widget_name = g_strdup_printf ("combobox-phone-%d", record);
-       phone_type_combo_box = e_builder_get_widget (editor->priv->builder, widget_name);
-       g_free (widget_name);
-
-       widget_name = g_strdup_printf ("entry-phone-%d", record);
-       phone_entry = e_builder_get_widget (editor->priv->builder, widget_name);
-       g_free (widget_name);
-
-       *phone      = g_strdup (gtk_entry_get_text (GTK_ENTRY (phone_entry)));
-       *phone_type = gtk_combo_box_get_active (GTK_COMBO_BOX (phone_type_combo_box));
-}
-
-static void
 fill_in_phone (EContactEditor *editor)
 {
        GList *phone_attr_list;
        GList *l;
-       gint   record_n;
+       GtkWidget *w;
+       EContactEditorDynTable *dyntable;
+       GtkListStore *data_store;
+       GtkTreeIter iter;
+
+       w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
+       dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
 
        /* Clear */
 
-       for (record_n = 1; record_n <= PHONE_SLOTS; record_n++) {
-               fill_in_phone_record (editor, record_n, NULL, -1);
-       }
+       e_contact_editor_dyntable_clear_data (dyntable);
 
        /* Fill in */
 
-       phone_attr_list = get_attributes_named (E_VCARD (editor->priv->contact), "TEL");
+       phone_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_TEL);
+       data_store = e_contact_editor_dyntable_extract_data (dyntable);
 
-       for (record_n = 1, l = phone_attr_list;
-               l && record_n <= PHONE_SLOTS; l = g_list_next (l)) {
+       for (l = phone_attr_list;l ; l = g_list_next (l)) {
                EVCardAttribute *attr = l->data;
                gchar           *phone;
                gint             slot;
+               gint            phone_type;
 
+               slot = get_ui_slot (attr);
+               phone_type = get_phone_type (attr);
                phone = e_vcard_attribute_get_value (attr);
-               slot = alloc_ui_slot (editor, "entry-phone", get_ui_slot (attr), PHONE_SLOTS);
-               if (slot < 1)
-                       break;
 
-               fill_in_phone_record (
-                       editor, slot, phone, get_phone_type (attr));
-
-               record_n++;
+               gtk_list_store_append (data_store, &iter);
+               gtk_list_store_set (data_store,&iter,
+                                  DYNTABLE_STORE_COLUMN_SORTORDER, slot,
+                                  DYNTABLE_STORE_COLUMN_SELECTED_ITEM, phone_type,
+                                  DYNTABLE_STORE_COLUMN_ENTRY_STRING, phone,
+                                  -1);
 
                g_free (phone);
        }
+
+       e_contact_editor_dyntable_fill_in_data (dyntable);
+       g_list_free_full (phone_attr_list, (GDestroyNotify) e_vcard_attribute_free);
+
+       expand_phone (editor, TRUE);
 }
 
 static void
@@ -1552,45 +1492,54 @@ extract_phone (EContactEditor *editor)
        GList *old_attr_list;
        GList *ll;
        gint   i;
+       GtkWidget *w;
+       EContactEditorDynTable *dyntable;
+       GtkListStore *data_store;
+       GtkTreeModel *tree_model;
+       GtkTreeIter iter;
+       gboolean valid;
 
-       for (i = 1; i <= PHONE_SLOTS; i++) {
-               gchar *phone;
-               gint   phone_type;
+       w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
+       dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
+       data_store = e_contact_editor_dyntable_extract_data (dyntable);
+       tree_model = GTK_TREE_MODEL (data_store);
 
-               extract_phone_record (editor, i, &phone, &phone_type);
+       valid = gtk_tree_model_get_iter_first (tree_model, &iter);
+       while (valid){
+               gint phone_type;
+               gchar *phone;
+               EVCardAttribute *attr;
 
-               if (!STRING_IS_EMPTY (phone)) {
-                       EVCardAttribute *attr;
+               attr = e_vcard_attribute_new ("", "TEL");
+               gtk_tree_model_get (tree_model,&iter,
+                                  DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &phone_type,
+                                  DYNTABLE_STORE_COLUMN_ENTRY_STRING, &phone,
+                                  -1);
 
-                       attr = e_vcard_attribute_new ("", "TEL");
+               if (phone_type >= 0) {
+                       const gchar *type_1;
+                       const gchar *type_2;
 
-                       if (phone_type >= 0) {
-                               const gchar *type_1;
-                               const gchar *type_2;
+                       phone_index_to_type (phone_type, &type_1, &type_2);
 
-                               phone_index_to_type (phone_type, &type_1, &type_2);
+                       e_vcard_attribute_add_param_with_value (
+                               attr, e_vcard_attribute_param_new (EVC_TYPE), type_1);
 
+                       if (type_2)
                                e_vcard_attribute_add_param_with_value (
-                                       attr, e_vcard_attribute_param_new (EVC_TYPE), type_1);
+                                       attr, e_vcard_attribute_param_new (EVC_TYPE), type_2);
+               }
 
-                               if (type_2)
-                                       e_vcard_attribute_add_param_with_value (
-                                               attr, e_vcard_attribute_param_new (EVC_TYPE), type_2);
+               e_vcard_attribute_add_value (attr, phone);
 
-                       }
-
-                       e_vcard_attribute_add_value (attr, phone);
-                       set_ui_slot (attr, i);
+               attr_list = g_list_append (attr_list, attr);
 
-                       attr_list = g_list_append (attr_list, attr);
-               }
-
-               g_free (phone);
+               valid = gtk_tree_model_iter_next (tree_model, &iter);
        }
 
        /* Splice in the old attributes, minus the PHONE_SLOTS first */
 
-       old_attr_list = get_attributes_named (E_VCARD (editor->priv->contact), "TEL");
+       old_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_TEL);
        for (ll = old_attr_list, i = 1; ll && i <= PHONE_SLOTS; i++) {
                e_vcard_attribute_free (ll->data);
                ll = g_list_delete_link (ll, ll);
@@ -1599,81 +1548,82 @@ extract_phone (EContactEditor *editor)
        old_attr_list = ll;
        attr_list = g_list_concat (attr_list, old_attr_list);
 
-       set_attributes_named (E_VCARD (editor->priv->contact), "TEL", attr_list);
+       e_contact_set_attributes (editor->priv->contact, E_CONTACT_TEL, attr_list);
 
-       free_attr_list (attr_list);
+       g_list_free_full (attr_list, (GDestroyNotify) e_vcard_attribute_free);
 }
 
 static void
-init_phone_record_type (EContactEditor *editor,
-                        gint record)
+init_phone_record_type (EContactEditor *editor)
 {
-       GtkWidget *phone_type_combo_box;
-       GtkWidget *phone_entry;
-       gchar     *widget_name;
-       gint       i;
+       GtkWidget *w;
        GtkListStore *store;
+       gint i;
+       EContactEditorDynTable *dyntable;
 
-       widget_name = g_strdup_printf ("entry-phone-%d", record);
-       phone_entry = e_builder_get_widget (editor->priv->builder, widget_name);
-       g_free (widget_name);
-
-       widget_name = g_strdup_printf ("combobox-phone-%d", record);
-       phone_type_combo_box = e_builder_get_widget (editor->priv->builder, widget_name);
-       g_free (widget_name);
-
-       init_item_sensitiveable_combo_box (GTK_COMBO_BOX (phone_type_combo_box));
-
-       store = GTK_LIST_STORE (
-               gtk_combo_box_get_model (
-               GTK_COMBO_BOX (phone_type_combo_box)));
+       w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
+       dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
+       store = e_contact_editor_dyntable_get_combo_store (dyntable);
 
        for (i = 0; i < G_N_ELEMENTS (phones); i++) {
                GtkTreeIter iter;
 
                gtk_list_store_append (store, &iter);
-               gtk_list_store_set (
-                       store, &iter,
-                       0, e_contact_pretty_name (phones[i].field_id),
-                       1, TRUE,
-                       -1);
+               gtk_list_store_set (store, &iter,
+                                   DYNTABLE_COMBO_COLUMN_TEXT, e_contact_pretty_name (phones[i].field_id),
+                                   DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE,
+                                   -1);
        }
 
-       g_signal_connect_swapped (
-               phone_type_combo_box, "changed",
-               G_CALLBACK (gtk_widget_grab_focus), phone_entry);
-       g_signal_connect (
-               phone_type_combo_box, "changed",
-               G_CALLBACK (object_changed), editor);
-       g_signal_connect (
-               phone_entry, "changed",
-               G_CALLBACK (object_changed), editor);
-       g_signal_connect_swapped (
-               phone_entry, "activate",
-               G_CALLBACK (entry_activated), editor);
+       e_contact_editor_dyntable_set_combo_defaults (dyntable, phones_default, G_N_ELEMENTS 
(phones_default));
+}
+
+static void
+row_added_phone (EContactEditorDynTable *dyntable, EContactEditor *editor)
+{
+       expand_phone (editor, TRUE);
 }
 
 static void
 init_phone (EContactEditor *editor)
 {
-       gint i;
+       GtkWidget *w;
+       EContactEditorDynTable *dyntable;
+
+       w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
+       dyntable = E_CONTACT_EDITOR_DYNTABLE (w);
 
-       expand_phone (editor, FALSE);
+       e_contact_editor_dyntable_set_max_entries (dyntable, PHONE_SLOTS);
+       e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE);
+       e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_PER_LINE);
 
-       for (i = 1; i <= PHONE_SLOTS; i++)
-               init_phone_record_type (editor, i);
+       g_signal_connect (
+               w, "changed",
+               G_CALLBACK (object_changed), editor);
+       g_signal_connect_swapped (
+               w, "activate",
+               G_CALLBACK (entry_activated), editor);
+       g_signal_connect (
+               w, "row-added",
+               G_CALLBACK (row_added_phone), editor);
+
+       init_phone_record_type (editor);
 }
 
 static void
-sensitize_phone_types (EContactEditor *editor,
-                       GtkWidget *combo_box)
+sensitize_phone_types (EContactEditor *editor)
 {
+       GtkWidget *w;
+       GtkListStore *listStore;
        GtkTreeModel *model;
        GtkTreeIter iter;
        gint i;
        gboolean valid;
 
-       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+       w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
+       listStore = e_contact_editor_dyntable_get_combo_store (E_CONTACT_EDITOR_DYNTABLE (w));
+       model = GTK_TREE_MODEL (listStore);
+
        valid = gtk_tree_model_get_iter_first (model, &iter);
 
        for (i = 0; i < G_N_ELEMENTS (phones); i++) {
@@ -1682,51 +1632,29 @@ sensitize_phone_types (EContactEditor *editor,
                        return;
                }
 
-               gtk_list_store_set (
-                       GTK_LIST_STORE (model), &iter,
-                       1, is_field_supported (editor, phones[i].field_id),
-                       -1);
+               gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                                   DYNTABLE_COMBO_COLUMN_SENSITIVE,
+                                   is_field_supported (editor, phones[i].field_id),
+                                   -1);
 
                valid = gtk_tree_model_iter_next (model, &iter);
        }
 }
 
 static void
-sensitize_phone_record (EContactEditor *editor,
-                        gint record,
-                        gboolean enabled)
-{
-       GtkWidget *phone_type_combo_box;
-       GtkWidget *phone_entry;
-       gchar     *widget_name;
-
-       widget_name = g_strdup_printf ("combobox-phone-%d", record);
-       phone_type_combo_box = e_builder_get_widget (editor->priv->builder, widget_name);
-       g_free (widget_name);
-
-       widget_name = g_strdup_printf ("entry-phone-%d", record);
-       phone_entry = e_builder_get_widget (editor->priv->builder, widget_name);
-       g_free (widget_name);
-
-       gtk_widget_set_sensitive (phone_type_combo_box, enabled);
-       gtk_editable_set_editable (GTK_EDITABLE (phone_entry), enabled);
-
-       sensitize_phone_types (editor, phone_type_combo_box);
-}
-
-static void
 sensitize_phone (EContactEditor *editor)
 {
-       gint i;
+       GtkWidget *w;
+       gboolean enabled = TRUE;
 
-       for (i = 1; i <= PHONE_SLOTS; i++) {
-               gboolean enabled = TRUE;
+       w = e_builder_get_widget (editor->priv->builder, "phone-dyntable");
 
-               if (!editor->priv->target_editable)
-                       enabled = FALSE;
+       if (!editor->priv->target_editable)
+               enabled = FALSE;
 
-               sensitize_phone_record (editor, i, enabled);
-       }
+       gtk_widget_set_sensitive (w, enabled);
+
+       sensitize_phone_types (editor);
 }
 
 static void
@@ -4159,11 +4087,7 @@ expand_web_toggle (EContactEditor *ce)
 static void
 expand_phone_toggle (EContactEditor *ce)
 {
-       GtkWidget *phone_ext_table;
-
-       phone_ext_table = e_builder_get_widget (
-               ce->priv->builder, "table-phone-extended");
-       expand_phone (ce, !gtk_widget_get_visible (phone_ext_table));
+       expand_phone (ce, !is_arrow_image_arrow_down (ce, "arrow-phone-expand"));
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]