Enlightenment CVS committal Author : lok Project : e17 Module : libs/etk
Dir : e17/libs/etk/src/lib Modified Files: etk_tree.c etk_tree.h Log Message: [Etk_Tree] sort and insert_sorted reworks =================================================================== RCS file: /cvs/e/e17/libs/etk/src/lib/etk_tree.c,v retrieving revision 1.99 retrieving revision 1.100 diff -u -3 -r1.99 -r1.100 --- etk_tree.c 27 Jun 2007 10:42:47 -0000 1.99 +++ etk_tree.c 10 Jul 2007 02:30:51 -0000 1.100 @@ -137,6 +137,15 @@ static void _etk_tree_row_select(Etk_Tree *tree, Etk_Tree_Row *row, Etk_Modifiers modifiers); static void _etk_tree_heapify(Etk_Tree *tree, Etk_Tree_Row **heap, int root, int size, int (*compare_cb)(Etk_Tree_Col *col, Etk_Tree_Row *row1, Etk_Tree_Row *row2, void *data), int asc, Etk_Tree_Col *col, void *data); +static void _etk_tree_qsort(Etk_Tree_Row *first, Etk_Tree_Row *last, Etk_Tree_Col *col, + int (*compare_cb)(Etk_Tree_Col *col, Etk_Tree_Row *row1, Etk_Tree_Row *row2, void *data), + void *data, Etk_Bool ascending); +static void _etk_tree_partition(Etk_Tree_Row **first, Etk_Tree_Row **last, Etk_Tree_Row *pivot, Etk_Tree_Col *col, + int (*compare_cb)(Etk_Tree_Col *col, Etk_Tree_Row *row1, Etk_Tree_Row *row2, void *data), + void *data, Etk_Bool ascending); +static void _etk_tree_row_move_relatively(Etk_Tree_Row *center, Etk_Tree_Row *row, Etk_Bool after); +static void _etk_tree_reverse(Etk_Tree *tree); +static void _etk_tree_reverse_rows(Etk_Tree_Row *first); static Etk_Signal *_etk_tree_signals[ETK_TREE_NUM_SIGNALS]; static Etk_Signal *_etk_tree_col_signals[ETK_TREE_COL_NUM_SIGNALS]; @@ -845,69 +854,46 @@ * @param col the column that will be passed to @a compare_cb when it is called * @param compare_cb the function to call to compare two rows. It should return a negative * value if the cell of "row1" has a lower value than the cell of "row2", 0 if they have the - * same value, and a positive value if the cell of "row2" has a greater value than the cell of "row1" + * same value, and a positive value if the cell of "row1" has a greater value than the cell of "row2" * @param data a pointer that will be passed to @a compare_cb when it is called * @param ascending Etk_True to perform an ascendant sort, ETK_FALSE to perform a descendant sort */ void etk_tree_col_sort_full(Etk_Tree_Col *col, int (*compare_cb)(Etk_Tree_Col *col, Etk_Tree_Row *row1, Etk_Tree_Row *row2, void *data), void *data, Etk_Bool ascending) { Etk_Tree *tree; - Etk_Tree_Row *first_row, *r; - Etk_Tree_Row **heap; - int num_rows; - int pos, parent, i; - int asc; - - if (!col || !compare_cb || !(tree = col->tree)) - return; - if (!(first_row = tree->root.first_child)) + Etk_Tree_Row *row, *to_sort; + + if (!col || !compare_cb || !(tree = col->tree) || (tree->sorted_col == col && tree->sorted_asc == ascending)) return; - - asc = ascending ? -1 : 1; - num_rows = tree->root.num_children; - heap = malloc(num_rows * sizeof(Etk_Tree_Row *)); - - /* We insert all the rows in the heap */ - first_row = - heap[0] = first_row; - for (i = 1, r = first_row->next; r; r = r->next, i++) - { - pos = i; - parent = (pos - 1) / 2; - - while (parent >= 0 && (compare_cb(col, heap[parent], r, data) * asc) < 0) - { - heap[pos] = heap[parent]; - pos = parent; - parent = parent ? (pos - 1) / 2 : -1; - } - heap[pos] = r; - } - - /* Then we extract them */ - first_row = heap[0]; - first_row->prev = NULL; - first_row->next = NULL; - r = first_row; - heap[0] = heap[num_rows - 1]; - _etk_tree_heapify(tree, heap, 0, num_rows - 1, compare_cb, asc, col, data); - for (i = num_rows - 2; i >= 0; i--) - { - r->next = heap[0]; - heap[0]->prev = r; - r = heap[0]; - heap[0] = heap[i]; - _etk_tree_heapify(tree, heap, 0, i, compare_cb, asc, col, data); - } - r->next = NULL; - - tree->root.first_child = first_row; - tree->root.last_child = r; - free(heap); - + + if (tree->sorted_col == col && tree->sorted_asc != ascending) + { + _etk_tree_reverse(col->tree); + return; + } + + row = tree->root.first_child; + + to_sort = row; + while (row || to_sort) + { + Etk_Tree_Row *next; + if (to_sort) + { + Etk_Tree_Row *first = to_sort->parent->first_child; + Etk_Tree_Row *last = to_sort->parent->last_child; + _etk_tree_qsort(first, last, col, compare_cb, data, ascending); + to_sort = NULL; + row = row->parent->first_child; + } + next = etk_tree_row_walk_next(row, ETK_TRUE); + if (next && next->parent == row) to_sort = next; + row = next; + } + tree->sorted_col = col; tree->sorted_asc = ascending; - + etk_widget_redraw_queue(ETK_WIDGET(tree)); } @@ -989,6 +975,60 @@ } /** + * @brief Insert a new row in a sorted column at the right place + * @param tree a tree + * @param parent the parent row of the row to insert. If @a after is not NULL, @a parent is optionnal (the parent of the + * new row will be the parent of @a after) + * @param ... an "Etk_Tree_Col *" followed by the value of the cell, then any number of "Etk_Tree_Col *"/Value pairs, + * and terminated by NULL. Note that, according to the model used by the column, a cell value can use several parameters + * @return Returns the new row, or NULL on failure + * @warning It will only work if the tree is already sorted. + */ +Etk_Tree_Row *etk_tree_row_insert_sorted(Etk_Tree *tree, Etk_Tree_Row *parent, ...) +{ + Etk_Tree_Row *row, *after; + Etk_Tree_Col *col; + va_list args; + int i, j, direction; + + if (!tree || !(col = tree->sorted_col)) + return NULL; + + va_start(args, parent); + if (!parent) + parent = &tree->root; + row = etk_tree_row_insert_valist(tree, parent, parent->last_child, args); + va_end(args); + + after = parent->first_child; + j = parent->num_children; + direction = 1; + do + { + j = j / 2 + j % 2; + + if (direction == 1) + for (i = 0; after && i < j; after = after->next, i++); + else if (direction == -1) + for (i = 0; after && i < j; after = after->prev, i++); + else + break; + + direction = col->sort.compare_cb(col, row, after, col->sort.data) * (tree->sorted_asc ? 1 : -1); + } + while (j != 1); + + if (direction == -1) + _etk_tree_row_move_relatively(after, row, ETK_FALSE); + else + _etk_tree_row_move_relatively(after, row, ETK_TRUE); + + tree->sorted_col = col; + + return row; +} + +/** * @brief Inserts a new row after an existing row * @param tree a tree * @param parent the parent row of the row to insert. If @a after is not NULL, @a parent is optionnal (the parent of the @@ -1078,6 +1118,7 @@ va_end(args2); + if (tree->sorted_col) tree->sorted_col = NULL; if (!tree->frozen) { etk_signal_emit_by_name("scroll-size-changed", ETK_OBJECT(tree->scroll_content), NULL); @@ -1172,6 +1213,8 @@ if (col->models[i]->cell_data_set) col->models[i]->cell_data_set(col->models[i], row->cells_data[col->id][i], &args2); } + if (col == row->tree->sorted_col) + row->tree->sorted_col = NULL; if (emit_signal) etk_signal_emit(_etk_tree_col_signals[ETK_TREE_COL_CELL_VALUE_CHANGED], ETK_OBJECT(col), NULL, row); } @@ -3600,6 +3643,120 @@ heap[root] = tmp; _etk_tree_heapify(tree, heap, max, size, compare_cb, asc, col, data); } +} + +/* Do a quick sort on a list of rows */ +static void _etk_tree_qsort(Etk_Tree_Row *first, Etk_Tree_Row *last, Etk_Tree_Col *col, + int (*compare_cb)(Etk_Tree_Col *col, Etk_Tree_Row *row1, Etk_Tree_Row *row2, void *data), + void *data, Etk_Bool ascending) +{ + Etk_Tree_Row *pivot; + + if (!first || !last || first == last || last->next == first) return; + + pivot = last; + + _etk_tree_partition(&first, &last, pivot, col, compare_cb, data, ascending); + _etk_tree_qsort(first, pivot->prev, col, compare_cb, data, ascending); + _etk_tree_qsort(pivot->next, last, col, compare_cb, data, ascending); +} + +/* Quick sort partition for rows lists */ +static void _etk_tree_partition(Etk_Tree_Row **first, Etk_Tree_Row **last, Etk_Tree_Row *pivot, Etk_Tree_Col *col, + int (*compare_cb)(Etk_Tree_Col *col, Etk_Tree_Row *row1, Etk_Tree_Row *row2, void *data), + void *data, Etk_Bool ascending) +{ + Etk_Tree_Row *row; + + row = *first; + + while (row != pivot) + { + Etk_Tree_Row *next; + next = row->next; + if (compare_cb(col, row, pivot, data) * (ascending ? 1 : -1) > 0) + { + if (row == *first) *first = row->next; + if (pivot == *last) *last = row; + _etk_tree_row_move_relatively(pivot, row, ETK_TRUE); + } + row = next; + } +} + +/* Move row before or after center */ +static void _etk_tree_row_move_relatively(Etk_Tree_Row *center, Etk_Tree_Row *row, Etk_Bool after) +{ + if (!center || !row || row == center) return; + + if (row->prev) row->prev->next = row->next; + else row->parent->first_child = row->next; + if (row->next) row->next->prev = row->prev; + else row->parent->last_child = row->prev; + + if (after) + { + if (center->next) center->next->prev = row; + else center->parent->last_child = row; + row->next = center->next; + center->next = row; + row->prev = center; + } + else + { + if (center->prev) center->prev->next = row; + else center->parent->first_child = row; + row->prev = center->prev; + center->prev = row; + row->next = center; + } +} + +/* Reverse the whole tree */ +static void _etk_tree_reverse(Etk_Tree *tree) +{ + Etk_Tree_Row *row, *to_reverse; + + if (!tree) + return; + + to_reverse = row = tree->root.first_child; + while (row || to_reverse) + { + Etk_Tree_Row *next; + if (to_reverse) + { + _etk_tree_reverse_rows(to_reverse); + to_reverse = NULL; + row = row->parent->first_child; + } + next = etk_tree_row_walk_next(row, ETK_TRUE); + if (next && next->parent == row) to_reverse = next; + row = next; + } + + tree->sorted_asc = !(tree->sorted_asc); + etk_widget_redraw_queue(ETK_WIDGET(tree)); +} + +/* Reverse a list of rows */ +static void _etk_tree_reverse_rows(Etk_Tree_Row *first) +{ + Etk_Tree_Row *row = first; + + if (!first || first->parent->num_children < 2) return; + + row->parent->first_child = row->parent->last_child; + row->parent->last_child = row; + + while (row) + { + Etk_Tree_Row *next; + next = row->next; + row->next = row->prev; + row->prev = next; + row = next; + } } /** @} */ =================================================================== RCS file: /cvs/e/e17/libs/etk/src/lib/etk_tree.h,v retrieving revision 1.35 retrieving revision 1.36 diff -u -3 -r1.35 -r1.36 --- etk_tree.h 11 Apr 2007 09:47:28 -0000 1.35 +++ etk_tree.h 10 Jul 2007 02:30:51 -0000 1.36 @@ -199,8 +199,8 @@ Etk_Tree_Row *etk_tree_row_prepend(Etk_Tree *tree, Etk_Tree_Row *parent, ...); Etk_Tree_Row *etk_tree_row_append(Etk_Tree *tree, Etk_Tree_Row *parent, ...); Etk_Tree_Row *etk_tree_row_insert(Etk_Tree *tree, Etk_Tree_Row *parent, Etk_Tree_Row *after, ...); +Etk_Tree_Row *etk_tree_row_insert_sorted(Etk_Tree *tree, Etk_Tree_Row *parent, ...); Etk_Tree_Row *etk_tree_row_insert_valist(Etk_Tree *tree, Etk_Tree_Row *parent, Etk_Tree_Row *after, va_list args); -/* TODO: Etk_Tree_Row *etk_tree_row_insert_sorted(Etk_Tree *tree, Etk_Tree_Row *parent, ...); */ void etk_tree_row_delete(Etk_Tree_Row *row); void etk_tree_clear(Etk_Tree *tree); /* TODO: way to insert a separator... */ ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ enlightenment-cvs mailing list enlightenment-cvs@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/enlightenment-cvs