1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * sort.c : Routines for sorting cell ranges
7 * JP Rosevear <jpr@arcavia.com>
10 * (C) 2000 Morten Welinder
12 #include <gnumeric-config.h>
17 #include "clipboard.h"
22 #include <goffice/goffice.h>
33 gnm_sort_data_destroy (GnmSortData
*data
)
35 g_free (data
->clauses
);
37 g_free (data
->locale
);
42 gnm_sort_data_length (GnmSortData
const *data
)
45 return range_height (data
->range
);
47 return range_width (data
->range
);
50 /* The routines to do the sorting */
52 sort_compare_cells (GnmCell
const *ca
, GnmCell
const *cb
,
53 GnmSortClause
*clause
, gboolean default_locale
)
57 GnmValDiff comp
= IS_EQUAL
;
69 ta
= VALUE_IS_EMPTY (a
) ? VALUE_EMPTY
: a
->v_any
.type
;
70 tb
= VALUE_IS_EMPTY (b
) ? VALUE_EMPTY
: b
->v_any
.type
;
72 if (ta
== VALUE_EMPTY
&& tb
!= VALUE_EMPTY
) {
73 comp
= clause
->asc
? IS_LESS
: IS_GREATER
;
74 } else if (tb
== VALUE_EMPTY
&& ta
!= VALUE_EMPTY
) {
75 comp
= clause
->asc
? IS_GREATER
: IS_LESS
;
76 } else if (ta
== VALUE_ERROR
&& tb
!= VALUE_ERROR
) {
78 } else if (tb
== VALUE_ERROR
&& ta
!= VALUE_ERROR
) {
81 comp
= default_locale
? value_compare (a
, b
, clause
->cs
)
82 : value_compare_no_cache (a
, b
, clause
->cs
);
85 if (comp
== IS_LESS
) {
86 ans
= clause
->asc
? 1 : -1;
87 } else if (comp
== IS_GREATER
) {
88 ans
= clause
->asc
? -1 : 1;
95 sort_compare_sets (GnmSortData
const *data
, int indexa
, int indexb
,
96 gboolean default_locale
)
101 while (clause
< data
->num_clause
) {
103 int offset
= data
->clauses
[clause
].offset
;
106 ca
= sheet_cell_get (data
->sheet
,
107 data
->range
->start
.col
+ offset
,
108 data
->range
->start
.row
+ indexa
);
109 cb
= sheet_cell_get (data
->sheet
,
110 data
->range
->start
.col
+ offset
,
111 data
->range
->start
.row
+ indexb
);
113 ca
= sheet_cell_get (data
->sheet
,
114 data
->range
->start
.col
+ indexa
,
115 data
->range
->start
.row
+ offset
);
116 cb
= sheet_cell_get (data
->sheet
,
117 data
->range
->start
.col
+ indexb
,
118 data
->range
->start
.row
+ offset
);
121 result
= sort_compare_cells (ca
, cb
, &(data
->clauses
[clause
]),
129 /* Items are identical; make sort stable by using the indices. */
130 return indexa
- indexb
;
134 sort_qsort_compare (void const *_a
, void const *_b
)
136 SortDataPerm
const *a
= (SortDataPerm
const *)_a
;
137 SortDataPerm
const *b
= (SortDataPerm
const *)_b
;
139 return sort_compare_sets (a
->data
, a
->index
, b
->index
, TRUE
);
143 sort_qsort_compare_in_locale (void const *_a
, void const *_b
)
145 SortDataPerm
const *a
= (SortDataPerm
const *)_a
;
146 SortDataPerm
const *b
= (SortDataPerm
const *)_b
;
148 return sort_compare_sets (a
->data
, a
->index
, b
->index
, FALSE
);
153 sort_permute_range (GnmSortData
const *data
, GnmRange
*range
, int adj
)
156 range
->start
.row
= data
->range
->start
.row
+ adj
;
157 range
->start
.col
= data
->range
->start
.col
;
158 range
->end
.row
= range
->start
.row
;
159 range
->end
.col
= data
->range
->end
.col
;
161 range
->start
.row
= data
->range
->start
.row
;
162 range
->start
.col
= data
->range
->start
.col
+ adj
;
163 range
->end
.row
= data
->range
->end
.row
;
164 range
->end
.col
= range
->start
.col
;
169 gnm_sort_permute_invert (int const *perm
, int length
)
173 rperm
= g_new (int, length
);
174 for (i
= 0; i
< length
; i
++)
184 sort_permute (GnmSortData
*data
, int const *perm
, int length
,
190 pt
.sheet
= data
->sheet
;
191 pt
.paste_flags
= PASTE_CONTENTS
| PASTE_COMMENTS
| PASTE_NO_RECALC
;
192 if (!data
->retain_formats
)
193 pt
.paste_flags
= pt
.paste_flags
| PASTE_FORMATS
;
196 g_printerr ("Permutation:");
197 for (i
= 0; i
< length
; i
++)
198 g_printerr (" %d", perm
[i
]);
202 rperm
= gnm_sort_permute_invert (perm
, length
);
204 for (i
= 0; i
< length
; i
++) {
205 GnmRange range1
, range2
;
206 GnmCellRegion
*rcopy1
, *rcopy2
= NULL
;
209 /* Special case: element is already in place. */
212 g_printerr (" Fixpoint: %d\n", i
);
217 /* Track a full cycle. */
218 sort_permute_range (data
, &range1
, i
);
219 rcopy1
= clipboard_copy_range (data
->sheet
, &range1
);
222 g_printerr (" Cycle:");
227 g_printerr (" %d", i1
);
232 sort_permute_range (data
, &range2
, i2
);
234 /* Don't copy the start of the loop; we did that above. */
235 rcopy2
= clipboard_copy_range (data
->sheet
, &range2
);
239 clipboard_paste_region (rcopy1
, &pt
, cc
);
240 cellregion_unref (rcopy1
);
242 /* This is one step behind. */
258 gnm_sort_position (GnmSortData
*data
, int *perm
, GOCmdContext
*cc
)
262 length
= gnm_sort_data_length (data
);
263 sort_permute (data
, perm
, length
, cc
);
267 gnm_sort_contents (GnmSortData
*data
, GOCmdContext
*cc
)
269 ColRowInfo
const *cra
;
271 int length
, real_length
, i
, cur
, *iperm
, *real
;
272 int const first
= data
->top
? data
->range
->start
.row
: data
->range
->start
.col
;
274 length
= gnm_sort_data_length (data
);
277 /* Discern the rows/cols to be actually sorted */
278 real
= g_new (int, length
);
279 for (i
= 0; i
< length
; i
++) {
281 ? sheet_row_get (data
->sheet
, first
+ i
)
282 : sheet_col_get (data
->sheet
, first
+ i
);
284 if (cra
&& !cra
->visible
) {
293 perm
= g_new (SortDataPerm
, real_length
);
294 for (i
= 0; i
< length
; i
++) {
297 perm
[cur
].data
= data
;
302 if (real_length
> 1) {
305 = g_strdup (go_setlocale (LC_ALL
, NULL
));
306 go_setlocale (LC_ALL
, data
->locale
);
308 qsort (perm
, real_length
, sizeof (SortDataPerm
),
310 (old_locale
, data
->locale
)
312 : sort_qsort_compare_in_locale
);
314 go_setlocale (LC_ALL
, old_locale
);
317 qsort (perm
, real_length
, sizeof (SortDataPerm
),
322 iperm
= g_new (int, length
);
323 for (i
= 0; i
< length
; i
++) {
325 iperm
[i
] = perm
[cur
].index
;
334 sort_permute (data
, iperm
, length
, cc
);
336 /* Make up for the PASTE_NO_RECALC. */
337 sheet_region_queue_recalc (data
->sheet
, data
->range
);
338 sheet_flag_status_update_range (data
->sheet
, data
->range
);
339 sheet_range_calc_spans (data
->sheet
, data
->range
,
340 data
->retain_formats
? GNM_SPANCALC_RENDER
: GNM_SPANCALC_RE_RENDER
);
341 sheet_redraw_all (data
->sheet
, FALSE
);
348 gnm_sort_data_copy (GnmSortData
*data
)
352 g_return_val_if_fail (data
!= NULL
, NULL
);
354 result
= g_memdup (data
, sizeof (GnmSortData
));
355 result
->range
= g_memdup (result
->range
, sizeof (GnmRange
));
356 result
->clauses
= g_memdup (result
->clauses
,
357 result
->num_clause
* sizeof (GnmSortClause
));
358 result
->locale
= g_strdup (result
->locale
);
364 gnm_sort_data_get_type (void)
369 t
= g_boxed_type_register_static ("GnmSortData",
370 (GBoxedCopyFunc
)gnm_sort_data_copy
,
371 (GBoxedFreeFunc
)gnm_sort_data_destroy
);