PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/evolution-3.5.4/widgets/table/e-table-sorting-utils.c

#
C | 492 lines | 362 code | 64 blank | 66 comment | 73 complexity | 57cd67ba96060004597310dc34de163e MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, MPL-2.0-no-copyleft-exception
  1. /*
  2. * This program is free software; you can redistribute it and/or
  3. * modify it under the terms of the GNU Lesser General Public
  4. * License as published by the Free Software Foundation; either
  5. * version 2 of the License, or (at your option) version 3.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. * Lesser General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU Lesser General Public
  13. * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14. *
  15. *
  16. * Authors:
  17. * Chris Lahey <clahey@ximian.com>
  18. *
  19. * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  20. *
  21. */
  22. #ifdef HAVE_CONFIG_H
  23. #include <config.h>
  24. #endif
  25. #include <string.h>
  26. #include <camel/camel.h>
  27. #include "e-util/e-util.h"
  28. #include "e-table-sorting-utils.h"
  29. #define d(x)
  30. /* This takes source rows. */
  31. static gint
  32. etsu_compare (ETableModel *source,
  33. ETableSortInfo *sort_info,
  34. ETableHeader *full_header,
  35. gint row1,
  36. gint row2,
  37. gpointer cmp_cache)
  38. {
  39. gint j;
  40. gint sort_count = e_table_sort_info_sorting_get_count (sort_info);
  41. gint comp_val = 0;
  42. gint ascending = 1;
  43. for (j = 0; j < sort_count; j++) {
  44. ETableSortColumn column = e_table_sort_info_sorting_get_nth (sort_info, j);
  45. ETableCol *col;
  46. col = e_table_header_get_column_by_col_idx (full_header, column.column);
  47. if (col == NULL)
  48. col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
  49. comp_val = (*col->compare)(e_table_model_value_at (source, col->compare_col, row1),
  50. e_table_model_value_at (source, col->compare_col, row2),
  51. cmp_cache);
  52. ascending = column.ascending;
  53. if (comp_val != 0)
  54. break;
  55. }
  56. if (comp_val == 0) {
  57. if (row1 < row2)
  58. comp_val = -1;
  59. if (row1 > row2)
  60. comp_val = 1;
  61. }
  62. if (!ascending)
  63. comp_val = -comp_val;
  64. return comp_val;
  65. }
  66. typedef struct {
  67. gint cols;
  68. gpointer *vals;
  69. gint *ascending;
  70. GCompareDataFunc *compare;
  71. gpointer cmp_cache;
  72. } ETableSortClosure;
  73. typedef struct {
  74. ETreeModel *tree;
  75. ETableSortInfo *sort_info;
  76. ETableHeader *full_header;
  77. gpointer cmp_cache;
  78. } ETreeSortClosure;
  79. /* FIXME: Make it not cache the second and later columns (as if anyone cares.) */
  80. static gint
  81. e_sort_callback (gconstpointer data1,
  82. gconstpointer data2,
  83. gpointer user_data)
  84. {
  85. gint row1 = *(gint *) data1;
  86. gint row2 = *(gint *) data2;
  87. ETableSortClosure *closure = user_data;
  88. gint j;
  89. gint sort_count = closure->cols;
  90. gint comp_val = 0;
  91. gint ascending = 1;
  92. for (j = 0; j < sort_count; j++) {
  93. comp_val = (*(closure->compare[j]))(closure->vals[closure->cols * row1 + j], closure->vals[closure->cols * row2 + j], closure->cmp_cache);
  94. ascending = closure->ascending[j];
  95. if (comp_val != 0)
  96. break;
  97. }
  98. if (comp_val == 0) {
  99. if (row1 < row2)
  100. comp_val = -1;
  101. if (row1 > row2)
  102. comp_val = 1;
  103. }
  104. if (!ascending)
  105. comp_val = -comp_val;
  106. return comp_val;
  107. }
  108. void
  109. e_table_sorting_utils_sort (ETableModel *source,
  110. ETableSortInfo *sort_info,
  111. ETableHeader *full_header,
  112. gint *map_table,
  113. gint rows)
  114. {
  115. gint total_rows;
  116. gint i;
  117. gint j;
  118. gint cols;
  119. ETableSortClosure closure;
  120. g_return_if_fail (source != NULL);
  121. g_return_if_fail (E_IS_TABLE_MODEL (source));
  122. g_return_if_fail (sort_info != NULL);
  123. g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
  124. g_return_if_fail (full_header != NULL);
  125. g_return_if_fail (E_IS_TABLE_HEADER (full_header));
  126. total_rows = e_table_model_row_count (source);
  127. cols = e_table_sort_info_sorting_get_count (sort_info);
  128. closure.cols = cols;
  129. closure.vals = g_new (gpointer , total_rows * cols);
  130. closure.ascending = g_new (int, cols);
  131. closure.compare = g_new (GCompareDataFunc, cols);
  132. closure.cmp_cache = e_table_sorting_utils_create_cmp_cache ();
  133. for (j = 0; j < cols; j++) {
  134. ETableSortColumn column = e_table_sort_info_sorting_get_nth (sort_info, j);
  135. ETableCol *col;
  136. col = e_table_header_get_column_by_col_idx (full_header, column.column);
  137. if (col == NULL)
  138. col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
  139. for (i = 0; i < rows; i++) {
  140. closure.vals[map_table[i] * cols + j] = e_table_model_value_at (source, col->compare_col, map_table[i]);
  141. }
  142. closure.compare[j] = col->compare;
  143. closure.ascending[j] = column.ascending;
  144. }
  145. g_qsort_with_data (
  146. map_table, rows, sizeof (gint), e_sort_callback, &closure);
  147. g_free (closure.vals);
  148. g_free (closure.ascending);
  149. g_free (closure.compare);
  150. e_table_sorting_utils_free_cmp_cache (closure.cmp_cache);
  151. }
  152. gboolean
  153. e_table_sorting_utils_affects_sort (ETableSortInfo *sort_info,
  154. ETableHeader *full_header,
  155. gint col)
  156. {
  157. gint j;
  158. gint cols;
  159. g_return_val_if_fail (sort_info != NULL, TRUE);
  160. g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), TRUE);
  161. g_return_val_if_fail (full_header != NULL, TRUE);
  162. g_return_val_if_fail (E_IS_TABLE_HEADER (full_header), TRUE);
  163. cols = e_table_sort_info_sorting_get_count (sort_info);
  164. for (j = 0; j < cols; j++) {
  165. ETableSortColumn column = e_table_sort_info_sorting_get_nth (sort_info, j);
  166. ETableCol *tablecol;
  167. tablecol = e_table_header_get_column_by_col_idx (full_header, column.column);
  168. if (tablecol == NULL)
  169. tablecol = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
  170. if (col == tablecol->compare_col)
  171. return TRUE;
  172. }
  173. return FALSE;
  174. }
  175. /* FIXME: This could be done in time log n instead of time n with a binary search. */
  176. gint
  177. e_table_sorting_utils_insert (ETableModel *source,
  178. ETableSortInfo *sort_info,
  179. ETableHeader *full_header,
  180. gint *map_table,
  181. gint rows,
  182. gint row)
  183. {
  184. gint i;
  185. gpointer cmp_cache = e_table_sorting_utils_create_cmp_cache ();
  186. i = 0;
  187. /* handle insertions when we have a 'sort group' */
  188. while (i < rows && etsu_compare (source, sort_info, full_header, map_table[i], row, cmp_cache) < 0)
  189. i++;
  190. e_table_sorting_utils_free_cmp_cache (cmp_cache);
  191. return i;
  192. }
  193. /* FIXME: This could be done in time log n instead of time n with a binary search. */
  194. gint
  195. e_table_sorting_utils_check_position (ETableModel *source,
  196. ETableSortInfo *sort_info,
  197. ETableHeader *full_header,
  198. gint *map_table,
  199. gint rows,
  200. gint view_row)
  201. {
  202. gint i;
  203. gint row;
  204. gpointer cmp_cache;
  205. i = view_row;
  206. row = map_table[i];
  207. cmp_cache = e_table_sorting_utils_create_cmp_cache ();
  208. i = view_row;
  209. if (i < rows - 1 && etsu_compare (source, sort_info, full_header, map_table[i + 1], row, cmp_cache) < 0) {
  210. i++;
  211. while (i < rows - 1 && etsu_compare (source, sort_info, full_header, map_table[i], row, cmp_cache) < 0)
  212. i++;
  213. } else if (i > 0 && etsu_compare (source, sort_info, full_header, map_table[i - 1], row, cmp_cache) > 0) {
  214. i--;
  215. while (i > 0 && etsu_compare (source, sort_info, full_header, map_table[i], row, cmp_cache) > 0)
  216. i--;
  217. }
  218. e_table_sorting_utils_free_cmp_cache (cmp_cache);
  219. return i;
  220. }
  221. /* This takes source rows. */
  222. static gint
  223. etsu_tree_compare (ETreeModel *source,
  224. ETableSortInfo *sort_info,
  225. ETableHeader *full_header,
  226. ETreePath path1,
  227. ETreePath path2,
  228. gpointer cmp_cache)
  229. {
  230. gint j;
  231. gint sort_count = e_table_sort_info_sorting_get_count (sort_info);
  232. gint comp_val = 0;
  233. gint ascending = 1;
  234. for (j = 0; j < sort_count; j++) {
  235. ETableSortColumn column = e_table_sort_info_sorting_get_nth (sort_info, j);
  236. ETableCol *col;
  237. col = e_table_header_get_column_by_col_idx (full_header, column.column);
  238. if (col == NULL)
  239. col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
  240. comp_val = (*col->compare)(e_tree_model_value_at (source, path1, col->compare_col),
  241. e_tree_model_value_at (source, path2, col->compare_col),
  242. cmp_cache);
  243. ascending = column.ascending;
  244. if (comp_val != 0)
  245. break;
  246. }
  247. if (!ascending)
  248. comp_val = -comp_val;
  249. return comp_val;
  250. }
  251. static gint
  252. e_sort_tree_callback (gconstpointer data1,
  253. gconstpointer data2,
  254. gpointer user_data)
  255. {
  256. ETreePath *path1 = *(ETreePath *) data1;
  257. ETreePath *path2 = *(ETreePath *) data2;
  258. ETreeSortClosure *closure = user_data;
  259. return etsu_tree_compare (closure->tree, closure->sort_info, closure->full_header, path1, path2, closure->cmp_cache);
  260. }
  261. void
  262. e_table_sorting_utils_tree_sort (ETreeModel *source,
  263. ETableSortInfo *sort_info,
  264. ETableHeader *full_header,
  265. ETreePath *map_table,
  266. gint count)
  267. {
  268. ETableSortClosure closure;
  269. gint cols;
  270. gint i, j;
  271. gint *map;
  272. ETreePath *map_copy;
  273. g_return_if_fail (source != NULL);
  274. g_return_if_fail (E_IS_TREE_MODEL (source));
  275. g_return_if_fail (sort_info != NULL);
  276. g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
  277. g_return_if_fail (full_header != NULL);
  278. g_return_if_fail (E_IS_TABLE_HEADER (full_header));
  279. cols = e_table_sort_info_sorting_get_count (sort_info);
  280. closure.cols = cols;
  281. closure.vals = g_new (gpointer , count * cols);
  282. closure.ascending = g_new (int, cols);
  283. closure.compare = g_new (GCompareDataFunc, cols);
  284. closure.cmp_cache = e_table_sorting_utils_create_cmp_cache ();
  285. for (j = 0; j < cols; j++) {
  286. ETableSortColumn column = e_table_sort_info_sorting_get_nth (sort_info, j);
  287. ETableCol *col;
  288. col = e_table_header_get_column_by_col_idx (full_header, column.column);
  289. if (col == NULL)
  290. col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
  291. for (i = 0; i < count; i++) {
  292. closure.vals[i * cols + j] = e_tree_model_sort_value_at (source, map_table[i], col->compare_col);
  293. }
  294. closure.ascending[j] = column.ascending;
  295. closure.compare[j] = col->compare;
  296. }
  297. map = g_new (int, count);
  298. for (i = 0; i < count; i++) {
  299. map[i] = i;
  300. }
  301. g_qsort_with_data (
  302. map, count, sizeof (gint), e_sort_callback, &closure);
  303. map_copy = g_new (ETreePath, count);
  304. for (i = 0; i < count; i++) {
  305. map_copy[i] = map_table[i];
  306. }
  307. for (i = 0; i < count; i++) {
  308. map_table[i] = map_copy[map[i]];
  309. }
  310. g_free (map);
  311. g_free (map_copy);
  312. g_free (closure.vals);
  313. g_free (closure.ascending);
  314. g_free (closure.compare);
  315. e_table_sorting_utils_free_cmp_cache (closure.cmp_cache);
  316. }
  317. /* FIXME: This could be done in time log n instead of time n with a binary search. */
  318. gint
  319. e_table_sorting_utils_tree_check_position (ETreeModel *source,
  320. ETableSortInfo *sort_info,
  321. ETableHeader *full_header,
  322. ETreePath *map_table,
  323. gint count,
  324. gint old_index)
  325. {
  326. gint i;
  327. ETreePath path;
  328. gpointer cmp_cache = e_table_sorting_utils_create_cmp_cache ();
  329. i = old_index;
  330. path = map_table[i];
  331. if (i < count - 1 && etsu_tree_compare (source, sort_info, full_header, map_table[i + 1], path, cmp_cache) < 0) {
  332. i++;
  333. while (i < count - 1 && etsu_tree_compare (source, sort_info, full_header, map_table[i], path, cmp_cache) < 0)
  334. i++;
  335. } else if (i > 0 && etsu_tree_compare (source, sort_info, full_header, map_table[i - 1], path, cmp_cache) > 0) {
  336. i--;
  337. while (i > 0 && etsu_tree_compare (source, sort_info, full_header, map_table[i], path, cmp_cache) > 0)
  338. i--;
  339. }
  340. e_table_sorting_utils_free_cmp_cache (cmp_cache);
  341. return i;
  342. }
  343. /* FIXME: This does not pay attention to making sure that it's a stable insert. This needs to be fixed. */
  344. gint
  345. e_table_sorting_utils_tree_insert (ETreeModel *source,
  346. ETableSortInfo *sort_info,
  347. ETableHeader *full_header,
  348. ETreePath *map_table,
  349. gint count,
  350. ETreePath path)
  351. {
  352. gsize start;
  353. gsize end;
  354. ETreeSortClosure closure;
  355. closure.tree = source;
  356. closure.sort_info = sort_info;
  357. closure.full_header = full_header;
  358. closure.cmp_cache = e_table_sorting_utils_create_cmp_cache ();
  359. e_bsearch (&path, map_table, count, sizeof (ETreePath), e_sort_tree_callback, &closure, &start, &end);
  360. e_table_sorting_utils_free_cmp_cache (closure.cmp_cache);
  361. return end;
  362. }
  363. /**
  364. * e_table_sorting_utils_create_cmp_cache:
  365. *
  366. * Creates a new compare cache, which is storing pairs of string keys and
  367. * string values. This can be accessed by
  368. * e_table_sorting_utils_lookup_cmp_cache() and
  369. * e_table_sorting_utils_add_to_cmp_cache().
  370. *
  371. * Returned pointer should be freed with
  372. * e_table_sorting_utils_free_cmp_cache().
  373. **/
  374. gpointer
  375. e_table_sorting_utils_create_cmp_cache (void)
  376. {
  377. return g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, g_free);
  378. }
  379. /**
  380. * e_table_sorting_utils_free_cmp_cache:
  381. * @cmp_cache: a compare cache; cannot be %NULL
  382. *
  383. * Frees a compare cache previously created with
  384. * e_table_sorting_utils_create_cmp_cache().
  385. **/
  386. void
  387. e_table_sorting_utils_free_cmp_cache (gpointer cmp_cache)
  388. {
  389. g_return_if_fail (cmp_cache != NULL);
  390. g_hash_table_destroy (cmp_cache);
  391. }
  392. /**
  393. * e_table_sorting_utils_add_to_cmp_cache:
  394. * @cmp_cache: a compare cache; cannot be %NULL
  395. * @key: unique key to a cache; cannot be %NULL
  396. * @value: value to store for a key
  397. *
  398. * Adds a new value for a given key to a compare cache. If such key
  399. * already exists in a cache then its value will be replaced.
  400. * Note: Given @value will be stolen and later freed with g_free.
  401. **/
  402. void
  403. e_table_sorting_utils_add_to_cmp_cache (gpointer cmp_cache,
  404. const gchar *key,
  405. gchar *value)
  406. {
  407. g_return_if_fail (cmp_cache != NULL);
  408. g_return_if_fail (key != NULL);
  409. g_hash_table_insert (cmp_cache, (gchar *) camel_pstring_strdup (key), value);
  410. }
  411. /**
  412. * e_table_sorting_utils_lookup_cmp_cache:
  413. * @cmp_cache: a compare cache
  414. * @key: unique key to a cache
  415. *
  416. * Lookups for a key in a compare cache, which is passed in GCompareDataFunc as 'data'.
  417. * Returns %NULL when not found or the cache wasn't provided, otherwise value stored
  418. * with a key.
  419. **/
  420. const gchar *
  421. e_table_sorting_utils_lookup_cmp_cache (gpointer cmp_cache,
  422. const gchar *key)
  423. {
  424. g_return_val_if_fail (key != NULL, NULL);
  425. if (!cmp_cache)
  426. return NULL;
  427. return g_hash_table_lookup (cmp_cache, key);
  428. }