2 * dialog-cell-sort.c: Implements Cell Sort dialog boxes.
5 * JP Rosevear <jpr@arcavia.com>
6 * Michael Meeks <michael@ximian.com>
7 * Andreas J. Guelzow <aguelzow@taliesin.ca>
8 * Morten Welinder <terra@gnome.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 #include <gnumeric-config.h>
25 #include <glib/gi18n-lib.h>
30 #include <workbook-view.h>
34 #include <selection.h>
35 #include <parse-util.h>
41 #include <sheet-view.h>
43 #include <gnumeric-conf.h>
44 #include <widgets/gnumeric-cell-renderer-toggle.h>
45 #include <widgets/gnumeric-expr-entry.h>
49 #include <gsf/gsf-impl-utils.h>
50 #include <gdk/gdkkeysyms.h>
51 #include <goffice/goffice.h>
53 #define CELL_SORT_KEY "cell-sort-dialog"
65 GtkWidget
*warning_dialog
;
66 GtkWidget
*cancel_button
;
69 GtkWidget
*down_button
;
70 GtkWidget
*add_button
;
71 GtkWidget
*delete_button
;
72 GtkWidget
*clear_button
;
73 GnmExprEntry
*range_entry
;
74 GnmExprEntry
*add_entry
;
76 GtkTreeView
*treeview
;
77 GtkTreeViewColumn
*header_column
;
78 GtkTreeSelection
*selection
;
79 GtkWidget
*cell_sort_row_rb
;
80 GtkWidget
*cell_sort_col_rb
;
81 GtkWidget
*cell_sort_header_check
;
82 GtkWidget
*retain_format_check
;
83 GdkPixbuf
*image_ascending
;
84 GdkPixbuf
*image_descending
;
85 GOLocaleSel
*locale_selector
;
98 ITEM_DESCENDING_IMAGE
,
106 static const gint MAX_MENU_SIZE
= 20;
111 gboolean done_submenu
;
112 SortFlowState
*state
;
113 } AddSortFieldMenuState
;
116 header_name (Sheet
*sheet
, int col
, int row
)
121 cell
= sheet_cell_get (sheet
, col
, row
);
123 str
= value_get_as_string (cell
->value
);
130 col_row_name (Sheet
*sheet
, int col
, int row
, gboolean header
, gboolean is_cols
)
136 str
= g_strdup_printf (_("Column %s"), col_name (col
));
138 str
= g_strdup_printf (_("Row %s"), row_name (row
));
141 cell
= sheet_cell_get (sheet
, col
, row
);
142 if (cell
&& !gnm_cell_is_blank (cell
)) {
143 gchar
*header_str
, *generic_str
= str
;
144 header_str
= value_get_as_string (cell
->value
);
145 str
= g_strdup_printf (_("%s (%s)"), header_str
, generic_str
);
147 g_free (generic_str
);
156 already_in_sort_fields(int index
, SortFlowState
*state
)
162 /* See if index is already in the sort fields */
163 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state
->model
),
164 &iter
, NULL
, item
)) {
165 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
166 ITEM_NUMBER
, &number
,
170 if (number
== index
) {
175 /* Here means not already in sort fields */
180 range_already_in_sort_criteria(gint start
, gint end
, SortFlowState
*state
)
183 for (i
=start
; i
<=end
; i
++) {
184 if (!already_in_sort_fields(i
, state
))
192 build_sort_field_menu (gint start
, gint end
, gint index
, GtkWidget
*menu
, SortFlowState
*state
, int used
);
195 cb_sort_field_menu_activate(GtkWidget
*item
, AddSortFieldMenuState
*menu_state
)
197 GtkWidget
*menu
= GTK_WIDGET (gtk_menu_item_get_submenu(GTK_MENU_ITEM (item
)));
199 if (menu_state
->done_submenu
== FALSE
) {
200 build_sort_field_menu(menu_state
->start
,
204 menu_state
->state
, 0);
205 menu_state
->done_submenu
= TRUE
;
210 set_button_sensitivity(SortFlowState
*state
)
214 if (state
->sel
== NULL
) {
215 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
219 items
= state
->is_cols
? (state
->sel
->v_range
.cell
.b
.row
-
220 state
->sel
->v_range
.cell
.a
.row
+ 1) :
221 (state
->sel
->v_range
.cell
.b
.col
-
222 state
->sel
->v_range
.cell
.a
.col
+ 1);
225 gtk_widget_set_sensitive (state
->ok_button
,
226 (state
->sort_items
!= 0) &&
228 gtk_widget_set_sensitive (state
->clear_button
, state
->sort_items
!= 0);
232 append_data (SortFlowState
*state
, int i
, int index
)
236 Sheet
*sheet
= state
->sel
->v_range
.cell
.a
.sheet
;
237 gboolean sort_asc
= gnm_conf_get_core_sort_default_ascending ();
239 header
= state
->is_cols
240 ? header_name (sheet
, i
, index
)
241 : header_name (sheet
, index
, i
);
243 ? col_row_name (sheet
, i
, index
, FALSE
, TRUE
)
244 : col_row_name (sheet
, index
, i
, FALSE
, FALSE
);
245 gtk_list_store_append (state
->model
, &iter
);
246 gtk_list_store_set (state
->model
, &iter
,
249 ITEM_DESCENDING
, !sort_asc
,
250 ITEM_DESCENDING_IMAGE
, sort_asc
? state
->image_ascending
251 : state
->image_descending
,
252 ITEM_CASE_SENSITIVE
, gnm_conf_get_core_sort_default_by_case (),
253 ITEM_SORT_BY_VALUE
, TRUE
,
254 ITEM_MOVE_FORMAT
, TRUE
,
263 cb_sort_field_selection(G_GNUC_UNUSED GtkWidget
*item
, AddSortFieldMenuState
*menu_state
)
265 append_data(menu_state
->state
,
268 /* Update sensitivity if this is the first sort item. */
269 if (menu_state
->state
->sort_items
== 1)
270 set_button_sensitivity(menu_state
->state
);
274 build_sort_field_menu (gint start
, gint end
, gint index
, GtkWidget
*menu
, SortFlowState
*state
, int used
)
276 Sheet
*sheet
= state
->sel
->v_range
.cell
.a
.sheet
;
284 AddSortFieldMenuState
*menu_state
;
287 menu_size
= 1 + end
- start
;
288 if (MAX_MENU_SIZE
< menu_size
- used
) {
290 gint balanced_submenu_size
;
292 submenu_size
= (menu_size
+ MAX_MENU_SIZE
- 1) / MAX_MENU_SIZE
;
293 balanced_submenu_size
= sqrt((double)
294 (menu_size
+ MAX_MENU_SIZE
- 1));
295 if (balanced_submenu_size
> submenu_size
)
296 submenu_size
= balanced_submenu_size
;
298 for (i
= start
; i
<= end
; i
+=submenu_size
) {
299 this_end
= i
+ submenu_size
- 1;
303 /* See if there are any fields in this range that aren't already
306 if (range_already_in_sort_criteria(i
, this_end
, state
))
309 str_start
= state
->is_cols
310 ? col_row_name (sheet
, i
, index
, state
->header
, TRUE
)
311 : col_row_name (sheet
, index
, i
, state
->header
, FALSE
);
313 str_end
= state
->is_cols
314 ? col_row_name (sheet
, this_end
, index
, state
->header
, TRUE
)
315 : col_row_name (sheet
, index
, this_end
, state
->header
, FALSE
);
317 str
= g_strdup_printf(_("%s to %s"), str_start
, str_end
);
321 item
= (GtkWidget
*) gtk_menu_item_new_with_label(str
);
322 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
323 gtk_widget_show (item
);
325 menu_state
= g_new(AddSortFieldMenuState
, 1);
326 menu_state
->start
= i
;
327 menu_state
->end
= this_end
;
328 menu_state
->index
= index
;
329 menu_state
->state
= state
;
330 menu_state
->done_submenu
= FALSE
;
331 submenu
= gtk_menu_new();
332 gtk_menu_item_set_submenu(GTK_MENU_ITEM (item
), submenu
);
333 g_signal_connect (item
, "activate",
334 G_CALLBACK (cb_sort_field_menu_activate
), menu_state
);
337 for (i
= start
; i
<= end
; i
++) {
338 if (FALSE
== already_in_sort_fields(i
, state
)) {
341 ? col_row_name (sheet
, i
, index
, state
->header
, TRUE
)
342 : col_row_name (sheet
, index
, i
, state
->header
, FALSE
);
343 item
= (GtkWidget
*) gtk_menu_item_new_with_label(str
);
344 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
345 gtk_widget_show (item
);
346 menu_state
= g_new(AddSortFieldMenuState
, 1);
347 menu_state
->start
= i
;
349 menu_state
->index
= index
;
350 menu_state
->state
= state
;
351 menu_state
->done_submenu
= FALSE
;
352 g_signal_connect (item
, "activate",
353 G_CALLBACK (cb_sort_field_selection
),
361 load_model_data (SortFlowState
*state
)
367 int limit
= gnm_conf_get_core_sort_dialog_max_initial_clauses ();
369 if (state
->is_cols
) {
370 start
= state
->sel
->v_range
.cell
.a
.col
;
371 end
= state
->sel
->v_range
.cell
.b
.col
;
372 index
= state
->sel
->v_range
.cell
.a
.row
;
374 start
= state
->sel
->v_range
.cell
.a
.row
;
375 end
= state
->sel
->v_range
.cell
.b
.row
;
376 index
= state
->sel
->v_range
.cell
.a
.col
;
379 gtk_list_store_clear (state
->model
);
380 state
->sort_items
= 0;
382 if (end
>= start
+ limit
)
383 end
= start
+ limit
- 1;
385 for (i
= start
; i
<= end
; i
++)
386 append_data (state
, i
, index
);
390 translate_range (GnmValue
*range
, SortFlowState
*state
)
392 state
->is_cols
= !gtk_toggle_button_get_active (
393 GTK_TOGGLE_BUTTON (state
->cell_sort_row_rb
));
394 state
->header
= gtk_toggle_button_get_active (
395 GTK_TOGGLE_BUTTON (state
->cell_sort_header_check
));
397 value_release (state
->sel
);
399 load_model_data(state
);
403 cb_sort_header_check(SortFlowState
*state
)
405 state
->header
= gtk_toggle_button_get_active (
406 GTK_TOGGLE_BUTTON (state
->cell_sort_header_check
));
408 gtk_tree_view_column_set_visible (state
->header_column
, state
->header
);
409 set_button_sensitivity (state
);
413 cb_update_to_new_range (SortFlowState
*state
)
417 range
= gnm_expr_entry_parse_as_value
418 (GNM_EXPR_ENTRY (state
->range_entry
), state
->sheet
);
420 if (state
->sel
!= NULL
) {
421 value_release (state
->sel
);
423 gtk_list_store_clear (state
->model
);
424 state
->sort_items
= 0;
427 translate_range (range
, state
);
428 set_button_sensitivity (state
);
432 cb_dialog_destroy (SortFlowState
*state
)
434 value_release (state
->sel
);
437 g_clear_object (&state
->model
);
438 g_clear_object (&state
->gui
);
440 wbcg_edit_finish (state
->wbcg
, WBC_EDIT_REJECT
, NULL
);
442 state
->dialog
= NULL
;
444 g_clear_object (&state
->image_ascending
);
445 g_clear_object (&state
->image_descending
);
451 cb_dialog_ok_clicked (SortFlowState
*state
)
453 GnmSortData
*data
, *data_copy
;
454 GnmSortClause
*array
, *this_array_item
;
457 gboolean descending
, case_sensitive
, sort_by_value
, move_format
;
462 array
= g_new (GnmSortClause
, state
->sort_items
);
463 this_array_item
= array
;
464 base
= (state
->is_cols
? state
->sel
->v_range
.cell
.a
.col
: state
->sel
->v_range
.cell
.a
.row
);
466 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state
->model
),
467 &iter
, NULL
, item
)) {
468 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
469 ITEM_DESCENDING
,&descending
,
470 ITEM_CASE_SENSITIVE
, &case_sensitive
,
471 ITEM_SORT_BY_VALUE
, &sort_by_value
,
472 ITEM_MOVE_FORMAT
, &move_format
,
473 ITEM_NUMBER
, &number
,
476 this_array_item
->offset
= number
- base
;
477 this_array_item
->asc
= !!descending
;
478 this_array_item
->cs
= case_sensitive
;
479 this_array_item
->val
= sort_by_value
;
484 data
= g_new (GnmSortData
, 1);
485 data
->sheet
= state
->sel
->v_range
.cell
.a
.sheet
;
486 data
->range
= g_new (GnmRange
, 1);
487 data
->range
= range_init (data
->range
, state
->sel
->v_range
.cell
.a
.col
488 + ((state
->header
&& !state
->is_cols
) ? 1 : 0),
489 state
->sel
->v_range
.cell
.a
.row
490 + ((state
->header
&& state
->is_cols
) ? 1 : 0),
491 state
->sel
->v_range
.cell
.b
.col
,
492 state
->sel
->v_range
.cell
.b
.row
);
493 data
->num_clause
= state
->sort_items
;
494 data
->clauses
= array
;
495 data
->top
= state
->is_cols
;
496 data
->retain_formats
= gtk_toggle_button_get_active (
497 GTK_TOGGLE_BUTTON (state
->retain_format_check
));
498 data
->locale
= go_locale_sel_get_locale (state
->locale_selector
);
500 data_copy
= gnm_sort_data_copy (data
);
501 text
= gnm_expr_entry_get_text (state
->range_entry
);
502 gnm_sheet_add_sort_setup
504 g_strdup((text
!= NULL
&& text
[0] != '\0') ? text
: "Other"),
507 cmd_sort (GNM_WBC (state
->wbcg
), data
);
509 gtk_widget_destroy (state
->dialog
);
514 cb_dialog_cancel_clicked (G_GNUC_UNUSED GtkWidget
*button
,
515 SortFlowState
*state
)
517 gtk_widget_destroy (state
->dialog
);
521 dialog_cell_sort_load_sort_setup (SortFlowState
*state
, GnmSortData
const *data
)
524 GnmSortClause
*this = data
->clauses
;
525 gint base
, max
, index
;
526 Sheet
*sheet
= state
->sel
->v_range
.cell
.a
.sheet
;
529 sheet
= state
->sheet
;
531 go_locale_sel_set_locale (state
->locale_selector
, data
->locale
);
533 gtk_toggle_button_set_active (
534 GTK_TOGGLE_BUTTON (state
->retain_format_check
), data
->retain_formats
);
536 gtk_toggle_button_set_active (
537 GTK_TOGGLE_BUTTON (state
->cell_sort_row_rb
), !data
->top
);
538 state
->is_cols
= data
->top
;
540 index
= (data
->top
? state
->sel
->v_range
.cell
.a
.row
: state
->sel
->v_range
.cell
.a
.col
);
541 base
= (data
->top
? state
->sel
->v_range
.cell
.a
.col
: state
->sel
->v_range
.cell
.a
.row
);
542 max
= (data
->top
? state
->sel
->v_range
.cell
.b
.col
: state
->sel
->v_range
.cell
.b
.row
);
543 gtk_list_store_clear (state
->model
);
544 state
->sort_items
= 0;
545 for (i
= 0; i
< data
->num_clause
; i
++) {
546 if (data
->clauses
[i
].offset
<= max
) {
549 int id
= data
->clauses
[i
].offset
+ base
;
551 header
= state
->is_cols
552 ? header_name (sheet
, id
, index
)
553 : header_name (sheet
, index
, id
);
554 str
= col_row_name (sheet
, id
, id
, FALSE
, state
->is_cols
);
556 gtk_list_store_append (state
->model
, &iter
);
557 gtk_list_store_set (state
->model
, &iter
,
560 ITEM_DESCENDING
, data
->clauses
[i
].asc
,
561 ITEM_DESCENDING_IMAGE
,
562 !data
->clauses
[i
].asc
563 ? state
->image_ascending
564 : state
->image_descending
,
565 ITEM_CASE_SENSITIVE
, data
->clauses
[i
].cs
,
566 ITEM_SORT_BY_VALUE
, data
->clauses
[i
].val
,
567 ITEM_MOVE_FORMAT
, TRUE
,
574 set_button_sensitivity (state
);
577 static GnmRange
const *
578 dialog_load_selection (SortFlowState
*state
, gboolean
*col_rb
)
580 GnmRange
const *first
;
581 GnmSortData
const *data
;
583 first
= selection_first_range (state
->sv
, NULL
, NULL
);
586 gtk_toggle_button_set_active (
587 GTK_TOGGLE_BUTTON (state
->cell_sort_col_rb
),
588 (*col_rb
= (first
->end
.row
- first
->start
.row
> first
->end
.col
- first
->start
.col
)));
589 gnm_expr_entry_load_from_range (state
->range_entry
,
590 state
->sheet
, first
);
592 gtk_toggle_button_set_active (
593 GTK_TOGGLE_BUTTON (state
->cell_sort_col_rb
),
596 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state
->cell_sort_header_check
),
597 sheet_range_has_heading
598 (state
->sheet
, first
, *col_rb
, FALSE
));
599 cb_sort_header_check (state
);
601 data
= gnm_sheet_find_sort_setup (state
->sheet
,
602 gnm_expr_entry_get_text (state
->range_entry
));
604 dialog_cell_sort_load_sort_setup (state
, data
);
606 cb_update_to_new_range (state
);
612 * cb_sort_selection_changed:
614 * Refreshes the buttons on a row (un)selection
617 cb_sort_selection_changed (SortFlowState
*state
)
619 GtkTreeIter iter
, test
;
621 if (!gtk_tree_selection_get_selected (state
->selection
, NULL
, &iter
)) {
622 gtk_widget_set_sensitive (state
->up_button
, FALSE
);
623 gtk_widget_set_sensitive (state
->down_button
, FALSE
);
624 gtk_widget_set_sensitive (state
->delete_button
, FALSE
);
629 gtk_widget_set_sensitive
631 gtk_tree_model_iter_previous (GTK_TREE_MODEL (state
->model
),
635 gtk_widget_set_sensitive
637 gtk_tree_model_iter_next (GTK_TREE_MODEL (state
->model
),
640 gtk_widget_set_sensitive (state
->delete_button
, TRUE
);
641 set_button_sensitivity (state
);
645 toggled (SortFlowState
*state
, const gchar
*path_string
, int column
)
647 GtkTreeModel
*model
= GTK_TREE_MODEL (state
->model
);
649 GtkTreePath
*path
= gtk_tree_path_new_from_string (path_string
);
652 if (gtk_tree_model_get_iter (model
, &iter
, path
)) {
653 gtk_tree_model_get (model
, &iter
, column
, &value
, -1);
655 gtk_list_store_set (GTK_LIST_STORE (model
),
656 &iter
, column
, value
, -1);
658 g_warning ("Did not get a valid iterator");
661 gtk_tree_path_free (path
);
665 move_cb (SortFlowState
*state
,
666 gboolean (*mover
) (GtkTreeModel
*, GtkTreeIter
*))
668 GtkTreeIter iter
, this_iter
;
670 if (!gtk_tree_selection_get_selected (state
->selection
, NULL
,
675 if (!mover (GTK_TREE_MODEL(state
->model
), &iter
))
678 gtk_list_store_swap (state
->model
, &this_iter
, &iter
);
679 cb_sort_selection_changed (state
);
683 cb_up (SortFlowState
*state
)
685 move_cb (state
, gtk_tree_model_iter_previous
);
689 cb_down (SortFlowState
*state
)
691 move_cb (state
, gtk_tree_model_iter_next
);
695 cb_delete_clicked (G_GNUC_UNUSED GtkWidget
*w
, SortFlowState
*state
)
697 GtkTreeIter iter
, iter2
;
700 if (!gtk_tree_selection_get_selected (state
->selection
, NULL
, &iter
))
704 ok
= gtk_tree_model_iter_next (GTK_TREE_MODEL (state
->model
), &iter2
);
707 ok
= gtk_tree_model_iter_previous (GTK_TREE_MODEL (state
->model
), &iter2
);
711 gtk_tree_selection_select_iter (state
->selection
, &iter2
);
713 gtk_list_store_remove (state
->model
, &iter
);
715 set_button_sensitivity (state
);
720 cb_clear_clicked (SortFlowState
*state
)
722 state
->sort_items
= 0;
723 gtk_list_store_clear (state
->model
);
724 set_button_sensitivity (state
);
728 build_sort_field_base_menu (SortFlowState
*state
)
734 GtkWidget
*menu
= gtk_menu_new ();
737 if (state
->sel
!= NULL
) {
738 if (state
->is_cols
) {
739 start
= state
->sel
->v_range
.cell
.a
.col
;
740 end
= state
->sel
->v_range
.cell
.b
.col
;
741 index
= state
->sel
->v_range
.cell
.a
.row
;
743 start
= state
->sel
->v_range
.cell
.a
.row
;
744 end
= state
->sel
->v_range
.cell
.b
.row
;
745 index
= state
->sel
->v_range
.cell
.a
.col
;
748 build_sort_field_menu (start
, end
, index
, menu
, state
,
751 items
= gtk_container_get_children (GTK_CONTAINER (menu
));
756 item
= (GtkWidget
*) gtk_menu_item_new_with_label(state
->is_cols
?
757 _("no available column"): _("no available row"));
758 gtk_widget_set_sensitive( GTK_WIDGET (item
), FALSE
);
759 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
760 gtk_widget_show (item
);
765 return GTK_MENU (menu
);
769 show_add_menu (SortFlowState
*state
)
771 gnumeric_popup_menu (build_sort_field_base_menu(state
),
776 cb_add_clicked (SortFlowState
*state
)
779 GnmSheetRange grange_sort
, grange_add
;
780 GnmRange intersection
;
785 gboolean had_items
= (state
->sort_items
> 0);
787 range_add
= gnm_expr_entry_parse_as_value
788 (GNM_EXPR_ENTRY (state
->add_entry
), state
->sheet
);
790 if (range_add
== NULL
) {
791 show_add_menu (state
);
795 g_return_if_fail (range_add
!= NULL
&& state
->sel
!= NULL
);
797 gnm_sheet_range_from_value (&grange_sort
, state
->sel
);
798 gnm_sheet_range_from_value (&grange_add
, range_add
);
800 value_release (range_add
);
802 if (range_intersection (&intersection
, &grange_sort
.range
, &grange_add
.range
)) {
804 if (state
->is_cols
) {
805 start
= intersection
.start
.col
;
806 end
= intersection
.end
.col
;
807 index
= state
->sel
->v_range
.cell
.a
.row
;
809 start
= intersection
.start
.row
;
810 end
= intersection
.end
.row
;
811 index
= state
->sel
->v_range
.cell
.a
.col
;
814 for (i
= start
; i
<= end
; i
++) {
818 gboolean found
= FALSE
;
821 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state
->model
),
822 &iter
, NULL
, item
)) {
823 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
824 ITEM_NUMBER
, &number
,
834 append_data (state
, i
, index
);
837 if (!had_items
&& (state
->sort_items
> 0))
838 set_button_sensitivity(state
);
840 show_add_menu (state
);
841 gnm_expr_entry_load_from_text (GNM_EXPR_ENTRY (state
->add_entry
), "");
845 cb_treeview_button_press(G_GNUC_UNUSED GtkWidget
*w
, GdkEvent
*event
, SortFlowState
*state
)
847 if ((event
->type
== GDK_BUTTON_PRESS
) &&
848 (event
->button
.button
== 3)) {
849 gnumeric_popup_menu (build_sort_field_base_menu(state
),
858 cb_treeview_keypress (G_GNUC_UNUSED GtkWidget
*w
, GdkEventKey
*event
,
859 SortFlowState
*state
)
861 gboolean ctrl
= (event
->state
& GDK_CONTROL_MASK
);
864 switch (event
->keyval
) {
866 case GDK_KEY_KP_Delete
:
867 cb_delete_clicked (w
, state
);
876 if (gtk_tree_selection_get_selected (state
->selection
, NULL
, &iter
) &&
877 gtk_tree_model_iter_previous (GTK_TREE_MODEL (state
->model
), &iter
))
878 gtk_tree_selection_select_iter (state
->selection
,
882 case GDK_KEY_KP_Down
:
889 if (gtk_tree_selection_get_selected (state
->selection
, NULL
, &iter
) &&
890 gtk_tree_model_iter_next (GTK_TREE_MODEL (state
->model
), &iter
))
891 gtk_tree_selection_select_iter (state
->selection
,
899 cb_toggled_descending (G_GNUC_UNUSED GtkCellRendererToggle
*cell
,
900 const gchar
*path_string
,
901 SortFlowState
*state
)
903 GtkTreeModel
*model
= GTK_TREE_MODEL (state
->model
);
905 GtkTreePath
*path
= gtk_tree_path_new_from_string (path_string
);
908 if (gtk_tree_model_get_iter (model
, &iter
, path
)) {
909 gtk_tree_model_get (model
, &iter
, ITEM_DESCENDING
, &value
, -1);
911 gtk_list_store_set (GTK_LIST_STORE (model
), &iter
,
912 ITEM_DESCENDING
, FALSE
,
913 ITEM_DESCENDING_IMAGE
, state
->image_ascending
,
916 gtk_list_store_set (GTK_LIST_STORE (model
), &iter
,
917 ITEM_DESCENDING
, TRUE
,
918 ITEM_DESCENDING_IMAGE
, state
->image_descending
,
922 g_warning ("Did not get a valid iterator");
924 gtk_tree_path_free (path
);
928 /* We are currently not supporting `by-value' vs not. */
930 cb_toggled_sort_by_value (G_GNUC_UNUSED GtkCellRendererToggle
*cell
,
931 const gchar
*path_string
,
932 SortFlowState
*state
)
934 toggled (state
, path_string
, ITEM_SORT_BY_VALUE
);
939 cb_toggled_case_sensitive (G_GNUC_UNUSED GtkCellRendererToggle
*cell
,
940 const gchar
*path_string
,
941 SortFlowState
*state
)
943 toggled (state
, path_string
, ITEM_CASE_SENSITIVE
);
948 dialog_init (SortFlowState
*state
)
952 GtkTreeViewColumn
*column
;
953 GtkCellRenderer
*renderer
;
956 grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
, "cell-sort-grid"));
957 /* setup range entry */
958 state
->range_entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
959 gnm_expr_entry_set_flags (state
->range_entry
,
962 gtk_widget_set_hexpand (GTK_WIDGET (state
->range_entry
), TRUE
);
963 gtk_grid_attach (grid
, GTK_WIDGET (state
->range_entry
),
965 gnm_editable_enters (GTK_WINDOW (state
->dialog
),
966 GTK_WIDGET (state
->range_entry
));
967 gnm_expr_entry_set_update_policy (state
->range_entry
, GNM_UPDATE_DISCONTINUOUS
);
968 gtk_widget_show (GTK_WIDGET (state
->range_entry
));
969 g_signal_connect_swapped (G_OBJECT (state
->range_entry
),
971 G_CALLBACK (cb_update_to_new_range
), state
);
973 state
->locale_selector
= GO_LOCALE_SEL (go_locale_sel_new ());
974 gtk_widget_set_hexpand (GTK_WIDGET (state
->locale_selector
), TRUE
);
975 gtk_widget_show_all (GTK_WIDGET (state
->locale_selector
));
976 gtk_grid_attach (grid
, GTK_WIDGET (state
->locale_selector
),
979 grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
, "cell-sort-spec-grid"));
980 /* setup add entry */
981 state
->add_entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
982 gnm_expr_entry_set_flags (state
->add_entry
,
985 gtk_widget_set_hexpand (GTK_WIDGET (state
->add_entry
), TRUE
);
986 gtk_grid_attach (grid
, GTK_WIDGET (state
->add_entry
),
988 gnm_editable_enters (GTK_WINDOW (state
->dialog
),
989 GTK_WIDGET (state
->add_entry
));
990 gtk_widget_show (GTK_WIDGET (state
->add_entry
));
992 /* Set-up tree view */
993 scrolled
= go_gtk_builder_get_widget (state
->gui
, "scrolled_cell_sort_list");
994 state
->model
= gtk_list_store_new (NUM_COLUMNS
, G_TYPE_STRING
,
995 G_TYPE_STRING
, G_TYPE_BOOLEAN
,
996 GDK_TYPE_PIXBUF
, G_TYPE_BOOLEAN
,
997 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
999 state
->treeview
= GTK_TREE_VIEW (
1000 gtk_tree_view_new_with_model (GTK_TREE_MODEL (state
->model
)));
1001 state
->selection
= gtk_tree_view_get_selection (state
->treeview
);
1002 gtk_tree_selection_set_mode (state
->selection
, GTK_SELECTION_BROWSE
);
1003 g_signal_connect_swapped (state
->selection
,
1005 G_CALLBACK (cb_sort_selection_changed
), state
);
1007 state
->header_column
= gtk_tree_view_column_new_with_attributes
1009 gtk_cell_renderer_text_new (),
1010 "text", ITEM_HEADER
, NULL
);
1011 gtk_tree_view_append_column (state
->treeview
, state
->header_column
);
1013 column
= gtk_tree_view_column_new_with_attributes
1015 gtk_cell_renderer_text_new (),
1016 "text", ITEM_NAME
, NULL
);
1017 gtk_tree_view_append_column (state
->treeview
, column
);
1019 renderer
= gnumeric_cell_renderer_toggle_new ();
1020 g_signal_connect (G_OBJECT (renderer
),
1022 G_CALLBACK (cb_toggled_descending
), state
);
1023 column
= gtk_tree_view_column_new_with_attributes ("",
1025 "active", ITEM_DESCENDING
,
1026 "pixbuf", ITEM_DESCENDING_IMAGE
,
1028 gtk_tree_view_append_column (state
->treeview
, column
);
1030 renderer
= gtk_cell_renderer_toggle_new ();
1031 g_signal_connect (G_OBJECT (renderer
),
1033 G_CALLBACK (cb_toggled_case_sensitive
), state
);
1034 column
= gtk_tree_view_column_new_with_attributes (_("Case Sensitive"),
1036 "active", ITEM_CASE_SENSITIVE
, NULL
);
1037 gtk_tree_view_append_column (state
->treeview
, column
);
1039 gtk_tree_view_columns_autosize (state
->treeview
);
1042 g_signal_connect (G_OBJECT (state
->treeview
),
1044 G_CALLBACK (cb_treeview_keypress
), state
);
1045 g_signal_connect (G_OBJECT (state
->treeview
),
1046 "button_press_event",
1047 G_CALLBACK (cb_treeview_button_press
), state
);
1049 /* We are currently not supporting `by-value' vs not. */
1050 renderer
= gtk_cell_renderer_toggle_new ();
1051 g_signal_connect (G_OBJECT (renderer
),
1053 G_CALLBACK (cb_toggled_sort_by_value
), state
);
1054 column
= gtk_tree_view_column_new_with_attributes (_("By Value"),
1056 "active", ITEM_SORT_BY_VALUE
, NULL
);
1057 gtk_tree_view_append_column (state
->treeview
, column
);
1060 gtk_tree_view_set_reorderable (state
->treeview
,TRUE
);
1062 gtk_container_add (GTK_CONTAINER (scrolled
), GTK_WIDGET (state
->treeview
));
1063 gtk_widget_show (GTK_WIDGET (state
->treeview
));
1065 /* Set-up other widgets */
1066 state
->cell_sort_row_rb
= go_gtk_builder_get_widget (state
->gui
, "cell_sort_row_rb");
1067 state
->cell_sort_col_rb
= go_gtk_builder_get_widget (state
->gui
, "cell_sort_col_rb");
1068 g_signal_connect_swapped (G_OBJECT (state
->cell_sort_row_rb
),
1070 G_CALLBACK (cb_update_to_new_range
), state
);
1072 state
->cell_sort_header_check
= go_gtk_builder_get_widget (state
->gui
,
1073 "cell_sort_header_check");
1074 g_signal_connect_swapped (G_OBJECT (state
->cell_sort_header_check
),
1076 G_CALLBACK (cb_sort_header_check
), state
);
1078 state
->retain_format_check
= go_gtk_builder_get_widget (state
->gui
,
1079 "retain_format_button");
1080 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state
->retain_format_check
),
1081 gnm_conf_get_core_sort_default_retain_formats ());
1084 /* Set-up buttons */
1085 state
->up_button
= go_gtk_builder_get_widget (state
->gui
, "up_button");
1086 g_signal_connect_swapped (G_OBJECT (state
->up_button
),
1088 G_CALLBACK (cb_up
), state
);
1089 state
->down_button
= go_gtk_builder_get_widget (state
->gui
, "down_button");
1090 g_signal_connect_swapped (G_OBJECT (state
->down_button
),
1092 G_CALLBACK (cb_down
), state
);
1093 state
->add_button
= go_gtk_builder_get_widget (state
->gui
, "add_button");
1094 g_signal_connect_swapped (G_OBJECT (state
->add_button
),
1096 G_CALLBACK (cb_add_clicked
), state
);
1097 gtk_widget_set_sensitive (state
->add_button
, TRUE
);
1098 state
->delete_button
= go_gtk_builder_get_widget (state
->gui
, "delete_button");
1099 g_signal_connect (G_OBJECT (state
->delete_button
),
1101 G_CALLBACK (cb_delete_clicked
), state
);
1102 gtk_widget_set_sensitive (state
->delete_button
, FALSE
);
1104 state
->clear_button
= go_gtk_builder_get_widget (state
->gui
, "clear_button");
1105 g_signal_connect_swapped (G_OBJECT (state
->clear_button
),
1107 G_CALLBACK (cb_clear_clicked
), state
);
1108 gtk_widget_set_sensitive (state
->clear_button
, FALSE
);
1110 gtk_button_set_alignment (GTK_BUTTON (state
->up_button
), 0., .5);
1111 gtk_button_set_alignment (GTK_BUTTON (state
->down_button
), 0., .5);
1112 gtk_button_set_alignment (GTK_BUTTON (state
->add_button
), 0., .5);
1113 gtk_button_set_alignment (GTK_BUTTON (state
->delete_button
), 0., .5);
1114 gtk_button_set_alignment (GTK_BUTTON (state
->clear_button
), 0., .5);
1115 gnm_init_help_button (
1116 go_gtk_builder_get_widget (state
->gui
, "help_button"),
1117 GNUMERIC_HELP_LINK_CELL_SORT
);
1119 state
->ok_button
= go_gtk_builder_get_widget (state
->gui
, "ok_button");
1120 g_signal_connect_swapped (G_OBJECT (state
->ok_button
),
1122 G_CALLBACK (cb_dialog_ok_clicked
), state
);
1123 state
->cancel_button
= go_gtk_builder_get_widget (state
->gui
, "cancel_button");
1124 g_signal_connect (G_OBJECT (state
->cancel_button
),
1126 G_CALLBACK (cb_dialog_cancel_clicked
), state
);
1128 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
1130 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
1132 /* Finish dialog signals */
1133 wbc_gtk_attach_guru (state
->wbcg
, state
->dialog
);
1134 g_object_set_data_full (G_OBJECT (state
->dialog
),
1135 "state", state
, (GDestroyNotify
) cb_dialog_destroy
);
1137 dialog_load_selection (state
, &col_rb
);
1139 cb_sort_selection_changed (state
);
1140 gnm_expr_entry_grab_focus(GNM_EXPR_ENTRY (state
->add_entry
), TRUE
);
1144 * Main entry point for the Cell Sort dialog box
1147 dialog_cell_sort (WBCGtk
*wbcg
)
1149 SortFlowState
*state
;
1152 g_return_if_fail (wbcg
!= NULL
);
1154 if (gnm_dialog_raise_if_exists (wbcg
, CELL_SORT_KEY
))
1157 gui
= gnm_gtk_builder_load ("res:ui/cell-sort.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
1161 state
= g_new (SortFlowState
, 1);
1163 state
->wb
= wb_control_get_workbook (GNM_WBC (wbcg
));
1164 state
->sv
= wb_control_cur_sheet_view (GNM_WBC (wbcg
));
1165 state
->sheet
= sv_sheet (state
->sv
);
1166 state
->warning_dialog
= NULL
;
1168 state
->sort_items
= 0;
1170 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "CellSort");
1172 state
->image_ascending
=
1173 go_gtk_widget_render_icon_pixbuf (state
->dialog
,
1174 "view-sort-ascending",
1175 GTK_ICON_SIZE_LARGE_TOOLBAR
);
1176 state
->image_descending
=
1177 go_gtk_widget_render_icon_pixbuf (state
->dialog
,
1178 "view-sort-descending",
1179 GTK_ICON_SIZE_LARGE_TOOLBAR
);
1180 dialog_init (state
);
1182 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
1185 gtk_widget_show (state
->dialog
);