Ejemplo n.º 1
0
gboolean
e_table_sorting_utils_affects_sort (ETableSortInfo *sort_info,
                                    ETableHeader *full_header,
                                    gint compare_col)
{
	gint j;
	gint cols;

	g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), TRUE);
	g_return_val_if_fail (E_IS_TABLE_HEADER (full_header), TRUE);

	cols = e_table_sort_info_sorting_get_count (sort_info);

	for (j = 0; j < cols; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;

		spec = e_table_sort_info_sorting_get_nth (
			sort_info, j, NULL);

		col = e_table_header_get_column_by_spec (full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (full_header) - 1;
			col = e_table_header_get_column (full_header, last);
		}

		if (compare_col == col->spec->compare_col)
			return TRUE;
	}

	return FALSE;
}
static void
etfci_start_drag (ETableFieldChooserItem *etfci,
                  GdkEvent *event,
                  gdouble x,
                  gdouble y)
{
	GtkWidget *widget = GTK_WIDGET (GNOME_CANVAS_ITEM (etfci)->canvas);
	GtkTargetList *list;
	GdkDragContext *context;
	ETableCol *ecol;
	cairo_surface_t *cs;
	cairo_t *cr;
	gint drag_col;
	gint button_height;

	GtkTargetEntry  etfci_drag_types[] = {
		{ (gchar *) TARGET_ETABLE_COL_TYPE, 0, TARGET_ETABLE_COL_HEADER },
	};

	if (etfci->combined_header == NULL)
		return;

	drag_col = etfci_find_button (etfci, y);

	if (drag_col < 0 || drag_col > e_table_header_count (etfci->combined_header))
		return;

	ecol = e_table_header_get_column (etfci->combined_header, drag_col);

	if (ecol->spec->disabled)
		return;

	etfci->drag_col = ecol->spec->model_col;

	etfci_drag_types[0].target = g_strdup_printf (
		"%s-%s", etfci_drag_types[0].target, etfci->dnd_code);
	d (g_print ("etfci - %s\n", etfci_drag_types[0].target));
	list = gtk_target_list_new (etfci_drag_types, G_N_ELEMENTS (etfci_drag_types));
	context = gtk_drag_begin (widget, list, GDK_ACTION_MOVE, 1, event);
	g_free ((gpointer) etfci_drag_types[0].target);

	button_height = e_table_header_compute_height (ecol, widget);
	cs = cairo_image_surface_create (
		CAIRO_FORMAT_ARGB32,
		etfci->width, button_height);
	cr = cairo_create (cs);

	e_table_header_draw_button (
		cr, ecol,
		widget, 0, 0,
		etfci->width, button_height,
		etfci->width, button_height,
		E_TABLE_COL_ARROW_NONE);

	gtk_drag_set_icon_surface (context, cs);

	cairo_surface_destroy (cs);
	cairo_destroy (cr);
	etfci->maybe_drag = FALSE;
}
/**
 * e_table_group_container_construct
 * @parent: The %GnomeCanvasGroup to create a child of.
 * @etgc: The %ETableGroupContainer.
 * @full_header: The full header of the %ETable.
 * @header: The current header of the %ETable.
 * @model: The %ETableModel of the %ETable.
 * @sort_info: The %ETableSortInfo of the %ETable.
 * @n: Which grouping level this is (Starts at 0 and sends n + 1 to any child %ETableGroups.
 *
 * This routine constructs the new %ETableGroupContainer.
 */
void
e_table_group_container_construct (GnomeCanvasGroup *parent, ETableGroupContainer *etgc,
				   ETableHeader *full_header,
				   ETableHeader     *header,
				   ETableModel *model, ETableSortInfo *sort_info, int n)
{
	ETableCol *col;
	ETableSortColumn column = e_table_sort_info_grouping_get_nth(sort_info, n);
	GtkStyle *style;

	col = e_table_header_get_column_by_col_idx(full_header, column.column);
	if (col == NULL)
		col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);

	e_table_group_construct (parent, E_TABLE_GROUP (etgc), full_header, header, model);
	etgc->ecol = col;
	g_object_ref (etgc->ecol);
	etgc->sort_info = sort_info;
	g_object_ref (etgc->sort_info);
	etgc->n = n;
	etgc->ascending = column.ascending;

	style = GTK_WIDGET (GNOME_CANVAS_ITEM (etgc)->canvas)->style;
	etgc->font_desc = pango_font_description_copy (style->font_desc);

	etgc->open = TRUE;
}
static void
etfci_reflow (GnomeCanvasItem *item,
              gint flags)
{
	ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (item);
	gdouble old_height;
	gint i;
	gint count;
	gdouble height = 0;

	etfci_rebuild_combined (etfci);

	old_height = etfci->height;

	count = e_table_header_count (etfci->combined_header);
	for (i = 0; i < count; i++) {
		ETableCol *ecol;

		ecol = e_table_header_get_column (etfci->combined_header, i);
		if (ecol->spec->disabled)
			continue;
		height += e_table_header_compute_height (
			ecol, GTK_WIDGET (GNOME_CANVAS_ITEM (etfci)->canvas));
	}

	etfci->height = height;

	if (old_height != etfci->height)
		e_canvas_item_request_parent_reflow (item);

	gnome_canvas_item_request_update (item);
}
Ejemplo n.º 5
0
inline static gint
view_to_model_col (ETableItem *eti,
                   gint col)
{
	ETableCol *ecol = e_table_header_get_column (eti->header, col);
	return ecol ? ecol->col_idx : -1;
}
Ejemplo n.º 6
0
static AtkObject *
eti_ref_child (AtkObject *accessible,
               gint index)
{
	ETableItem *item;
	gint col, row;

	g_return_val_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (accessible), NULL);
	item = E_TABLE_ITEM (eti_a11y_get_gobject (accessible));
	if (!item)
		return NULL;

	if (index < item->cols) {
		ETableCol *ecol;
		AtkObject *child;

		ecol = e_table_header_get_column (item->header, index);
		child = gal_a11y_e_table_column_header_new (ecol, item, accessible);
		return child;
	}
	index -= item->cols;

	col = index % item->cols;
	row = index / item->cols;

	return eti_ref_at (ATK_TABLE (accessible), row, col);
}
Ejemplo n.º 7
0
inline static gint
view_to_model_col (ETableItem *eti,
                   gint view_col)
{
	ETableCol *ecol = e_table_header_get_column (eti->header, view_col);

	return (ecol != NULL) ? ecol->spec->model_col : -1;
}
static void
etfci_rebuild_combined (ETableFieldChooserItem *etfci)
{
	gint count;
	GHashTable *hash;
	gint i;

	if (etfci->combined_header != NULL)
		g_object_unref (etfci->combined_header);

	etfci->combined_header = e_table_header_new ();

	hash = g_hash_table_new (NULL, NULL);

	count = e_table_header_count (etfci->header);
	for (i = 0; i < count; i++) {
		ETableCol *ecol = e_table_header_get_column (etfci->header, i);
		if (ecol->spec->disabled)
			continue;
		g_hash_table_add (
			hash, GINT_TO_POINTER (ecol->spec->model_col));
	}

	count = e_table_header_count (etfci->full_header);
	for (i = 0; i < count; i++) {
		ETableCol *ecol;
		gpointer key;

		ecol = e_table_header_get_column (etfci->full_header, i);
		key = GINT_TO_POINTER (ecol->spec->model_col);

		if (ecol->spec->disabled)
			continue;

		if (!g_hash_table_contains (hash, key))
			e_table_header_add_column (
				etfci->combined_header, ecol, -1);
	}

	g_hash_table_destroy (hash);
}
Ejemplo n.º 9
0
ETableCol *
e_table_util_calculate_current_search_col (ETableHeader *header,
                                           ETableHeader *full_header,
                                           ETableSortInfo *sort_info,
                                           gboolean always_search)
{
	gint i;
	gint count;
	ETableCol *col = NULL;
	count = e_table_sort_info_grouping_get_count (sort_info);
	for (i = 0; i < count; i++) {
		ETableSortColumn column = e_table_sort_info_grouping_get_nth (sort_info, i);

		col = e_table_header_get_column (full_header, column.column);

		if (col && col->search)
			break;

		col = NULL;
	}

	if (col == NULL) {
		count = e_table_sort_info_sorting_get_count (sort_info);
		for (i = 0; i < count; i++) {
			ETableSortColumn column = e_table_sort_info_sorting_get_nth (sort_info, i);

			col = e_table_header_get_column (full_header, column.column);

			if (col && col->search)
				break;

			col = NULL;
		}
	}

	if (col == NULL && always_search) {
		col = e_table_header_prioritized_column_selected (header, check_col, NULL);
	}

	return col;
}
Ejemplo n.º 10
0
/* atk table */
static AtkObject *
eti_ref_at (AtkTable *table,
            gint row,
            gint column)
{
	ETableItem *item;
	AtkObject * ret;
	GalA11yETableItemPrivate *priv = GET_PRIVATE (table);

	if (atk_state_set_contains_state (priv->state_set, ATK_STATE_DEFUNCT))
		return NULL;

	item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
	if (!item)
		return NULL;

	if (column >= 0 &&
	    column < item->cols &&
	    row >= 0 &&
	    row < item->rows &&
	    item->cell_views_realized) {
		ECellView *cell_view = item->cell_views[column];
		ETableCol *ecol = e_table_header_get_column (item->header, column);
		ret = gal_a11y_e_cell_registry_get_object (
			NULL,
			item,
			cell_view,
			ATK_OBJECT (table),
			ecol->spec->model_col,
			column,
			row);
		if (ATK_IS_OBJECT (ret)) {
			g_object_weak_ref (
				G_OBJECT (ret),
				(GWeakNotify) cell_destroyed,
				ret);
			/* if current cell is focused, add FOCUSED state */
			if (e_selection_model_cursor_row (item->selection) ==
				GAL_A11Y_E_CELL (ret)->row &&
				e_selection_model_cursor_col (item->selection) ==
				GAL_A11Y_E_CELL (ret)->model_col)
				gal_a11y_e_cell_add_state (
					GAL_A11Y_E_CELL (ret),
					ATK_STATE_FOCUSED, FALSE);
		} else
			ret = NULL;

		return ret;
	}

	return NULL;
}
Ejemplo n.º 11
0
/* This takes source rows. */
static gint
etsu_compare (ETableModel *source,
              ETableSortInfo *sort_info,
              ETableHeader *full_header,
              gint row1,
              gint row2,
              gpointer cmp_cache)
{
	gint j;
	gint sort_count = e_table_sort_info_sorting_get_count (sort_info);
	gint comp_val = 0;
	GtkSortType sort_type = GTK_SORT_ASCENDING;

	for (j = 0; j < sort_count; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;
		gpointer value1, value2;

		spec = e_table_sort_info_sorting_get_nth (
			sort_info, j, &sort_type);

		col = e_table_header_get_column_by_spec (full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (full_header) - 1;
			col = e_table_header_get_column (full_header, last);
		}

		value1 = e_table_model_value_at (source, col->spec->compare_col, row1);
		value2 = e_table_model_value_at (source, col->spec->compare_col, row2);

		comp_val = (*col->compare) (value1, value2, cmp_cache);

		e_table_model_free_value (source, col->spec->compare_col, value1);
		e_table_model_free_value (source, col->spec->compare_col, value2);

		if (comp_val != 0)
			break;
	}

	if (comp_val == 0) {
		if (row1 < row2)
			comp_val = -1;
		if (row1 > row2)
			comp_val = 1;
	}

	if (sort_type == GTK_SORT_DESCENDING)
		comp_val = -comp_val;

	return comp_val;
}
Ejemplo n.º 12
0
inline static gint
model_to_view_col (ETableItem *eti,
                   gint model_col)
{
	gint i;
	if (model_col == -1)
		return -1;
	for (i = 0; i < eti->cols; i++) {
		ETableCol *ecol = e_table_header_get_column (eti->header, i);
		if (ecol->spec->model_col == model_col)
			return i;
	}
	return -1;
}
static void
etfci_draw (GnomeCanvasItem *item,
            cairo_t *cr,
            gint x,
            gint y,
            gint width,
            gint height)
{
	ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (item);
	GnomeCanvas *canvas = item->canvas;
	gint rows;
	gint y1, y2;
	gint row;

	if (etfci->combined_header == NULL)
		return;

	rows = e_table_header_count (etfci->combined_header);

	y1 = y2 = 0;
	for (row = 0; row < rows; row++, y1 = y2) {
		ETableCol *ecol;

		ecol = e_table_header_get_column (etfci->combined_header, row);

		if (ecol->spec->disabled)
			continue;

		y2 += e_table_header_compute_height (ecol, GTK_WIDGET (canvas));

		if (y1 > (y + height))
			break;

		if (y2 < y)
			continue;

		cairo_save (cr);

		e_table_header_draw_button (
			cr, ecol,
			GTK_WIDGET (canvas),
			-x, y1 - y,
			width, height,
			etfci->width, y2 - y1,
			E_TABLE_COL_ARROW_NONE);

		cairo_restore (cr);
	}
}
Ejemplo n.º 14
0
static const gchar *
eti_get_column_description (AtkTable *table,
                            gint column)
{
	ETableItem *item;
	ETableCol *ecol;

	item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
	if (!item)
		return NULL;

	ecol = e_table_header_get_column (item->header, column);

	return ecol->text;
}
Ejemplo n.º 15
0
/* Static functions */
static const gchar *
gal_a11y_e_cell_get_name (AtkObject *a11y)
{
	GalA11yECell *cell = GAL_A11Y_E_CELL (a11y);
	ETableCol *ecol;

	if (a11y->name != NULL && strcmp (a11y->name, ""))
		return a11y->name;

	if (cell->item != NULL) {
		ecol = e_table_header_get_column (cell->item->header, cell->view_col);
		if (ecol != NULL)
			return ecol->text;
	}

	return _("Table Cell");
}
Ejemplo n.º 16
0
static AtkObject *
eti_get_column_header (AtkTable *table,
                       gint column)
{
	ETableItem *item;
	ETableCol *ecol;
	AtkObject *atk_obj = NULL;

	item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
	if (!item)
		return NULL;

	ecol = e_table_header_get_column (item->header, column);
	if (ecol) {
		atk_obj = gal_a11y_e_table_column_header_new (ecol, item, ATK_OBJECT (table));
	}

	return atk_obj;
}
Ejemplo n.º 17
0
ETableHeader *
e_table_state_to_header (GtkWidget *widget,
                         ETableHeader *full_header,
                         ETableState *state)
{
	ETableHeader *nh;
	const gint max_cols = e_table_header_count (full_header);
	gint column;
	GValue *val = g_new0 (GValue, 1);

	g_return_val_if_fail (widget, NULL);
	g_return_val_if_fail (full_header, NULL);
	g_return_val_if_fail (state, NULL);

	nh = e_table_header_new ();
	g_value_init (val, G_TYPE_DOUBLE);
	g_value_set_double (val, e_table_header_width_extras (widget));
	g_object_set_property (G_OBJECT(nh), "width_extras", val);
	g_free (val);

	for (column = 0; column < state->col_count; column++) {
		gint col;
		gdouble expansion;
		ETableCol *table_col;

		col = state->columns[column];
		expansion = state->expansions[column];

		if (col >= max_cols)
			continue;

		table_col = e_table_header_get_column (full_header, col);

		if (expansion >= -1)
			table_col->expansion = expansion;

		e_table_header_add_column (nh, table_col, -1);
	}

	return nh;
}
Ejemplo n.º 18
0
/* This takes source rows. */
static gint
etsu_tree_compare (ETreeModel *source,
                   ETableSortInfo *sort_info,
                   ETableHeader *full_header,
                   ETreePath path1,
                   ETreePath path2,
                   gpointer cmp_cache)
{
	gint j;
	gint sort_count = e_table_sort_info_sorting_get_count (sort_info);
	gint comp_val = 0;
	GtkSortType sort_type = GTK_SORT_ASCENDING;

	for (j = 0; j < sort_count; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;

		spec = e_table_sort_info_sorting_get_nth (
			sort_info, j, &sort_type);

		col = e_table_header_get_column_by_spec (full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (full_header) - 1;
			col = e_table_header_get_column (full_header, last);
		}

		comp_val = (*col->compare) (
			e_tree_model_value_at (
				source, path1, col->spec->compare_col),
			e_tree_model_value_at (
				source, path2, col->spec->compare_col),
			cmp_cache);
		if (comp_val != 0)
			break;
	}

	if (sort_type == GTK_SORT_DESCENDING)
		comp_val = -comp_val;

	return comp_val;
}
static gint
etfci_find_button (ETableFieldChooserItem *etfci,
                   gdouble loc)
{
	gint i;
	gint count;
	gdouble height = 0;

	count = e_table_header_count (etfci->combined_header);
	for (i = 0; i < count; i++) {
		ETableCol *ecol;

		ecol = e_table_header_get_column (etfci->combined_header, i);
		if (ecol->spec->disabled)
			continue;
		height += e_table_header_compute_height (
			ecol, GTK_WIDGET (GNOME_CANVAS_ITEM (etfci)->canvas));
		if (height > loc)
			return i;
	}
	return MAX (0, count - 1);
}
Ejemplo n.º 20
0
void
e_table_sorting_utils_sort (ETableModel *source,
                            ETableSortInfo *sort_info,
                            ETableHeader *full_header,
                            gint *map_table,
                            gint rows)
{
	gint total_rows;
	gint i;
	gint j;
	gint cols;
	ETableSortClosure closure;

	g_return_if_fail (E_IS_TABLE_MODEL (source));
	g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
	g_return_if_fail (E_IS_TABLE_HEADER (full_header));

	total_rows = e_table_model_row_count (source);
	cols = e_table_sort_info_sorting_get_count (sort_info);
	closure.cols = cols;

	closure.vals = g_new (gpointer, total_rows * cols);
	closure.sort_type = g_new (GtkSortType, cols);
	closure.compare = g_new (GCompareDataFunc, cols);
	closure.cmp_cache = e_table_sorting_utils_create_cmp_cache ();

	for (j = 0; j < cols; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;

		spec = e_table_sort_info_sorting_get_nth (
			sort_info, j, &closure.sort_type[j]);

		col = e_table_header_get_column_by_spec (full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (full_header) - 1;
			col = e_table_header_get_column (full_header, last);
		}

		for (i = 0; i < rows; i++) {
			closure.vals[map_table[i] * cols + j] = e_table_model_value_at (source, col->spec->compare_col, map_table[i]);
		}
		closure.compare[j] = col->compare;
	}

	g_qsort_with_data (
		map_table, rows, sizeof (gint), e_sort_callback, &closure);

	for (j = 0; j < cols; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;

		spec = e_table_sort_info_sorting_get_nth (
			sort_info, j, &closure.sort_type[j]);

		col = e_table_header_get_column_by_spec (full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (full_header) - 1;
			col = e_table_header_get_column (full_header, last);
		}

		for (i = 0; i < rows; i++) {
			e_table_model_free_value (source, col->spec->compare_col, closure.vals[map_table[i] * cols + j]);
		}
	}

	g_free (closure.vals);
	g_free (closure.sort_type);
	g_free (closure.compare);
	e_table_sorting_utils_free_cmp_cache (closure.cmp_cache);
}
Ejemplo n.º 21
0
void
e_table_sorting_utils_tree_sort (ETreeModel *source,
                                 ETableSortInfo *sort_info,
                                 ETableHeader *full_header,
                                 ETreePath *map_table,
                                 gint count)
{
	ETableSortClosure closure;
	gint cols;
	gint i, j;
	gint *map;
	ETreePath *map_copy;

	g_return_if_fail (E_IS_TREE_MODEL (source));
	g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
	g_return_if_fail (E_IS_TABLE_HEADER (full_header));

	cols = e_table_sort_info_sorting_get_count (sort_info);
	closure.cols = cols;

	closure.vals = g_new (gpointer , count * cols);
	closure.sort_type = g_new (GtkSortType, cols);
	closure.compare = g_new (GCompareDataFunc, cols);
	closure.cmp_cache = e_table_sorting_utils_create_cmp_cache ();

	for (j = 0; j < cols; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;

		spec = e_table_sort_info_sorting_get_nth (
			sort_info, j, &closure.sort_type[j]);

		col = e_table_header_get_column_by_spec (full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (full_header) - 1;
			col = e_table_header_get_column (full_header, last);
		}

		for (i = 0; i < count; i++) {
			closure.vals[i * cols + j] = e_tree_model_sort_value_at (source, map_table[i], col->spec->compare_col);
		}
		closure.compare[j] = col->compare;
	}

	map = g_new (int, count);
	for (i = 0; i < count; i++) {
		map[i] = i;
	}

	g_qsort_with_data (
		map, count, sizeof (gint), e_sort_callback, &closure);

	map_copy = g_new (ETreePath, count);
	for (i = 0; i < count; i++) {
		map_copy[i] = map_table[i];
	}
	for (i = 0; i < count; i++) {
		map_table[i] = map_copy[map[i]];
	}

	for (j = 0; j < cols; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;

		spec = e_table_sort_info_sorting_get_nth (
			sort_info, j, &closure.sort_type[j]);

		col = e_table_header_get_column_by_spec (full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (full_header) - 1;
			col = e_table_header_get_column (full_header, last);
		}

		for (i = 0; i < count; i++) {
			e_tree_model_free_value (source, col->spec->compare_col, closure.vals[i * cols + j]);
		}
	}

	g_free (map);
	g_free (map_copy);

	g_free (closure.vals);
	g_free (closure.sort_type);
	g_free (closure.compare);
	e_table_sorting_utils_free_cmp_cache (closure.cmp_cache);
}
Ejemplo n.º 22
0
static void
table_sorter_sort (ETableSorter *table_sorter)
{
	gint rows;
	gint i;
	gint j;
	gint cols;
	gint group_cols;
	struct qsort_data qd;

	if (table_sorter->sorted)
		return;

	rows = e_table_model_row_count (table_sorter->source);
	group_cols = e_table_sort_info_grouping_get_count (table_sorter->sort_info);
	cols = e_table_sort_info_sorting_get_count (table_sorter->sort_info) + group_cols;

	table_sorter->sorted = g_new (int, rows);
	for (i = 0; i < rows; i++)
		table_sorter->sorted[i] = i;

	qd.cols = cols;
	qd.table_sorter = table_sorter;

	qd.vals = g_new (gpointer , rows * cols);
	qd.ascending = g_new (int, cols);
	qd.compare = g_new (GCompareDataFunc, cols);
	qd.cmp_cache = e_table_sorting_utils_create_cmp_cache ();

	for (j = 0; j < cols; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;
		GtkSortType sort_type;

		if (j < group_cols)
			spec = e_table_sort_info_grouping_get_nth (
				table_sorter->sort_info,
				j, &sort_type);
		else
			spec = e_table_sort_info_sorting_get_nth (
				table_sorter->sort_info,
				j - group_cols, &sort_type);

		col = e_table_header_get_column_by_spec (
			table_sorter->full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (
				table_sorter->full_header) - 1;
			col = e_table_header_get_column (
				table_sorter->full_header, last);
		}

		for (i = 0; i < rows; i++) {
			qd.vals[i * cols + j] = e_table_model_value_at (
				table_sorter->source,
				col->spec->model_col, i);
		}

		qd.compare[j] = col->compare;
		qd.ascending[j] = (sort_type == GTK_SORT_ASCENDING);
	}

	g_qsort_with_data (table_sorter->sorted, rows, sizeof (gint), qsort_callback, &qd);

	for (j = 0; j < cols; j++) {
		ETableColumnSpecification *spec;
		ETableCol *col;
		GtkSortType sort_type;

		if (j < group_cols)
			spec = e_table_sort_info_grouping_get_nth (
				table_sorter->sort_info,
				j, &sort_type);
		else
			spec = e_table_sort_info_sorting_get_nth (
				table_sorter->sort_info,
				j - group_cols, &sort_type);

		col = e_table_header_get_column_by_spec (
			table_sorter->full_header, spec);
		if (col == NULL) {
			gint last = e_table_header_count (
				table_sorter->full_header) - 1;
			col = e_table_header_get_column (
				table_sorter->full_header, last);
		}

		for (i = 0; i < rows; i++) {
			e_table_model_free_value (table_sorter->source, col->spec->model_col, qd.vals[i * cols + j]);
		}
	}

	g_free (qd.vals);
	g_free (qd.ascending);
	g_free (qd.compare);
	e_table_sorting_utils_free_cmp_cache (qd.cmp_cache);
}