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 * Refreshes the buttons on a row (un)selection
616 cb_sort_selection_changed (SortFlowState
*state
)
618 GtkTreeIter iter
, test
;
620 if (!gtk_tree_selection_get_selected (state
->selection
, NULL
, &iter
)) {
621 gtk_widget_set_sensitive (state
->up_button
, FALSE
);
622 gtk_widget_set_sensitive (state
->down_button
, FALSE
);
623 gtk_widget_set_sensitive (state
->delete_button
, FALSE
);
628 gtk_widget_set_sensitive
630 gtk_tree_model_iter_previous (GTK_TREE_MODEL (state
->model
),
634 gtk_widget_set_sensitive
636 gtk_tree_model_iter_next (GTK_TREE_MODEL (state
->model
),
639 gtk_widget_set_sensitive (state
->delete_button
, TRUE
);
640 set_button_sensitivity (state
);
644 toggled (SortFlowState
*state
, const gchar
*path_string
, int column
)
646 GtkTreeModel
*model
= GTK_TREE_MODEL (state
->model
);
648 GtkTreePath
*path
= gtk_tree_path_new_from_string (path_string
);
651 if (gtk_tree_model_get_iter (model
, &iter
, path
)) {
652 gtk_tree_model_get (model
, &iter
, column
, &value
, -1);
654 gtk_list_store_set (GTK_LIST_STORE (model
),
655 &iter
, column
, value
, -1);
657 g_warning ("Did not get a valid iterator");
660 gtk_tree_path_free (path
);
664 move_cb (SortFlowState
*state
,
665 gboolean (*mover
) (GtkTreeModel
*, GtkTreeIter
*))
667 GtkTreeIter iter
, this_iter
;
669 if (!gtk_tree_selection_get_selected (state
->selection
, NULL
,
674 if (!mover (GTK_TREE_MODEL(state
->model
), &iter
))
677 gtk_list_store_swap (state
->model
, &this_iter
, &iter
);
678 cb_sort_selection_changed (state
);
682 cb_up (SortFlowState
*state
)
684 move_cb (state
, gtk_tree_model_iter_previous
);
688 cb_down (SortFlowState
*state
)
690 move_cb (state
, gtk_tree_model_iter_next
);
694 cb_delete_clicked (G_GNUC_UNUSED GtkWidget
*w
, SortFlowState
*state
)
696 GtkTreeIter iter
, iter2
;
699 if (!gtk_tree_selection_get_selected (state
->selection
, NULL
, &iter
))
703 ok
= gtk_tree_model_iter_next (GTK_TREE_MODEL (state
->model
), &iter2
);
706 ok
= gtk_tree_model_iter_previous (GTK_TREE_MODEL (state
->model
), &iter2
);
710 gtk_tree_selection_select_iter (state
->selection
, &iter2
);
712 gtk_list_store_remove (state
->model
, &iter
);
714 set_button_sensitivity (state
);
719 cb_clear_clicked (SortFlowState
*state
)
721 state
->sort_items
= 0;
722 gtk_list_store_clear (state
->model
);
723 set_button_sensitivity (state
);
727 build_sort_field_base_menu (SortFlowState
*state
)
733 GtkWidget
*menu
= gtk_menu_new ();
736 if (state
->sel
!= NULL
) {
737 if (state
->is_cols
) {
738 start
= state
->sel
->v_range
.cell
.a
.col
;
739 end
= state
->sel
->v_range
.cell
.b
.col
;
740 index
= state
->sel
->v_range
.cell
.a
.row
;
742 start
= state
->sel
->v_range
.cell
.a
.row
;
743 end
= state
->sel
->v_range
.cell
.b
.row
;
744 index
= state
->sel
->v_range
.cell
.a
.col
;
747 build_sort_field_menu (start
, end
, index
, menu
, state
,
750 items
= gtk_container_get_children (GTK_CONTAINER (menu
));
755 item
= (GtkWidget
*) gtk_menu_item_new_with_label(state
->is_cols
?
756 _("no available column"): _("no available row"));
757 gtk_widget_set_sensitive( GTK_WIDGET (item
), FALSE
);
758 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
759 gtk_widget_show (item
);
764 return GTK_MENU (menu
);
768 show_add_menu (SortFlowState
*state
)
770 gnumeric_popup_menu (build_sort_field_base_menu(state
),
775 cb_add_clicked (SortFlowState
*state
)
778 GnmSheetRange grange_sort
, grange_add
;
779 GnmRange intersection
;
784 gboolean had_items
= (state
->sort_items
> 0);
786 range_add
= gnm_expr_entry_parse_as_value
787 (GNM_EXPR_ENTRY (state
->add_entry
), state
->sheet
);
789 if (range_add
== NULL
) {
790 show_add_menu (state
);
794 g_return_if_fail (range_add
!= NULL
&& state
->sel
!= NULL
);
796 gnm_sheet_range_from_value (&grange_sort
, state
->sel
);
797 gnm_sheet_range_from_value (&grange_add
, range_add
);
799 value_release (range_add
);
801 if (range_intersection (&intersection
, &grange_sort
.range
, &grange_add
.range
)) {
803 if (state
->is_cols
) {
804 start
= intersection
.start
.col
;
805 end
= intersection
.end
.col
;
806 index
= state
->sel
->v_range
.cell
.a
.row
;
808 start
= intersection
.start
.row
;
809 end
= intersection
.end
.row
;
810 index
= state
->sel
->v_range
.cell
.a
.col
;
813 for (i
= start
; i
<= end
; i
++) {
817 gboolean found
= FALSE
;
820 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state
->model
),
821 &iter
, NULL
, item
)) {
822 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
823 ITEM_NUMBER
, &number
,
833 append_data (state
, i
, index
);
836 if (!had_items
&& (state
->sort_items
> 0))
837 set_button_sensitivity(state
);
839 show_add_menu (state
);
840 gnm_expr_entry_load_from_text (GNM_EXPR_ENTRY (state
->add_entry
), "");
844 cb_treeview_button_press(G_GNUC_UNUSED GtkWidget
*w
, GdkEvent
*event
, SortFlowState
*state
)
846 if ((event
->type
== GDK_BUTTON_PRESS
) &&
847 (event
->button
.button
== 3)) {
848 gnumeric_popup_menu (build_sort_field_base_menu(state
),
857 cb_treeview_keypress (G_GNUC_UNUSED GtkWidget
*w
, GdkEventKey
*event
,
858 SortFlowState
*state
)
860 gboolean ctrl
= (event
->state
& GDK_CONTROL_MASK
);
863 switch (event
->keyval
) {
865 case GDK_KEY_KP_Delete
:
866 cb_delete_clicked (w
, state
);
875 if (gtk_tree_selection_get_selected (state
->selection
, NULL
, &iter
) &&
876 gtk_tree_model_iter_previous (GTK_TREE_MODEL (state
->model
), &iter
))
877 gtk_tree_selection_select_iter (state
->selection
,
881 case GDK_KEY_KP_Down
:
888 if (gtk_tree_selection_get_selected (state
->selection
, NULL
, &iter
) &&
889 gtk_tree_model_iter_next (GTK_TREE_MODEL (state
->model
), &iter
))
890 gtk_tree_selection_select_iter (state
->selection
,
898 cb_toggled_descending (G_GNUC_UNUSED GtkCellRendererToggle
*cell
,
899 const gchar
*path_string
,
900 SortFlowState
*state
)
902 GtkTreeModel
*model
= GTK_TREE_MODEL (state
->model
);
904 GtkTreePath
*path
= gtk_tree_path_new_from_string (path_string
);
907 if (gtk_tree_model_get_iter (model
, &iter
, path
)) {
908 gtk_tree_model_get (model
, &iter
, ITEM_DESCENDING
, &value
, -1);
910 gtk_list_store_set (GTK_LIST_STORE (model
), &iter
,
911 ITEM_DESCENDING
, FALSE
,
912 ITEM_DESCENDING_IMAGE
, state
->image_ascending
,
915 gtk_list_store_set (GTK_LIST_STORE (model
), &iter
,
916 ITEM_DESCENDING
, TRUE
,
917 ITEM_DESCENDING_IMAGE
, state
->image_descending
,
921 g_warning ("Did not get a valid iterator");
923 gtk_tree_path_free (path
);
927 /* We are currently not supporting `by-value' vs not. */
929 cb_toggled_sort_by_value (G_GNUC_UNUSED GtkCellRendererToggle
*cell
,
930 const gchar
*path_string
,
931 SortFlowState
*state
)
933 toggled (state
, path_string
, ITEM_SORT_BY_VALUE
);
938 cb_toggled_case_sensitive (G_GNUC_UNUSED GtkCellRendererToggle
*cell
,
939 const gchar
*path_string
,
940 SortFlowState
*state
)
942 toggled (state
, path_string
, ITEM_CASE_SENSITIVE
);
947 dialog_init (SortFlowState
*state
)
951 GtkTreeViewColumn
*column
;
952 GtkCellRenderer
*renderer
;
955 grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
, "cell-sort-grid"));
956 /* setup range entry */
957 state
->range_entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
958 gnm_expr_entry_set_flags (state
->range_entry
,
961 gtk_widget_set_hexpand (GTK_WIDGET (state
->range_entry
), TRUE
);
962 gtk_grid_attach (grid
, GTK_WIDGET (state
->range_entry
),
964 gnm_editable_enters (GTK_WINDOW (state
->dialog
),
965 GTK_WIDGET (state
->range_entry
));
966 gnm_expr_entry_set_update_policy (state
->range_entry
, GNM_UPDATE_DISCONTINUOUS
);
967 gtk_widget_show (GTK_WIDGET (state
->range_entry
));
968 g_signal_connect_swapped (G_OBJECT (state
->range_entry
),
970 G_CALLBACK (cb_update_to_new_range
), state
);
972 state
->locale_selector
= GO_LOCALE_SEL (go_locale_sel_new ());
973 gtk_widget_set_hexpand (GTK_WIDGET (state
->locale_selector
), TRUE
);
974 gtk_widget_show_all (GTK_WIDGET (state
->locale_selector
));
975 gtk_grid_attach (grid
, GTK_WIDGET (state
->locale_selector
),
978 grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
, "cell-sort-spec-grid"));
979 /* setup add entry */
980 state
->add_entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
981 gnm_expr_entry_set_flags (state
->add_entry
,
984 gtk_widget_set_hexpand (GTK_WIDGET (state
->add_entry
), TRUE
);
985 gtk_grid_attach (grid
, GTK_WIDGET (state
->add_entry
),
987 gnm_editable_enters (GTK_WINDOW (state
->dialog
),
988 GTK_WIDGET (state
->add_entry
));
989 gtk_widget_show (GTK_WIDGET (state
->add_entry
));
991 /* Set-up tree view */
992 scrolled
= go_gtk_builder_get_widget (state
->gui
, "scrolled_cell_sort_list");
993 state
->model
= gtk_list_store_new (NUM_COLUMNS
, G_TYPE_STRING
,
994 G_TYPE_STRING
, G_TYPE_BOOLEAN
,
995 GDK_TYPE_PIXBUF
, G_TYPE_BOOLEAN
,
996 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
998 state
->treeview
= GTK_TREE_VIEW (
999 gtk_tree_view_new_with_model (GTK_TREE_MODEL (state
->model
)));
1000 state
->selection
= gtk_tree_view_get_selection (state
->treeview
);
1001 gtk_tree_selection_set_mode (state
->selection
, GTK_SELECTION_BROWSE
);
1002 g_signal_connect_swapped (state
->selection
,
1004 G_CALLBACK (cb_sort_selection_changed
), state
);
1006 state
->header_column
= gtk_tree_view_column_new_with_attributes
1008 gtk_cell_renderer_text_new (),
1009 "text", ITEM_HEADER
, NULL
);
1010 gtk_tree_view_append_column (state
->treeview
, state
->header_column
);
1012 column
= gtk_tree_view_column_new_with_attributes
1014 gtk_cell_renderer_text_new (),
1015 "text", ITEM_NAME
, NULL
);
1016 gtk_tree_view_append_column (state
->treeview
, column
);
1018 renderer
= gnumeric_cell_renderer_toggle_new ();
1019 g_signal_connect (G_OBJECT (renderer
),
1021 G_CALLBACK (cb_toggled_descending
), state
);
1022 column
= gtk_tree_view_column_new_with_attributes ("",
1024 "active", ITEM_DESCENDING
,
1025 "pixbuf", ITEM_DESCENDING_IMAGE
,
1027 gtk_tree_view_append_column (state
->treeview
, column
);
1029 renderer
= gtk_cell_renderer_toggle_new ();
1030 g_signal_connect (G_OBJECT (renderer
),
1032 G_CALLBACK (cb_toggled_case_sensitive
), state
);
1033 column
= gtk_tree_view_column_new_with_attributes (_("Case Sensitive"),
1035 "active", ITEM_CASE_SENSITIVE
, NULL
);
1036 gtk_tree_view_append_column (state
->treeview
, column
);
1038 gtk_tree_view_columns_autosize (state
->treeview
);
1041 g_signal_connect (G_OBJECT (state
->treeview
),
1043 G_CALLBACK (cb_treeview_keypress
), state
);
1044 g_signal_connect (G_OBJECT (state
->treeview
),
1045 "button_press_event",
1046 G_CALLBACK (cb_treeview_button_press
), state
);
1048 /* We are currently not supporting `by-value' vs not. */
1049 renderer
= gtk_cell_renderer_toggle_new ();
1050 g_signal_connect (G_OBJECT (renderer
),
1052 G_CALLBACK (cb_toggled_sort_by_value
), state
);
1053 column
= gtk_tree_view_column_new_with_attributes (_("By Value"),
1055 "active", ITEM_SORT_BY_VALUE
, NULL
);
1056 gtk_tree_view_append_column (state
->treeview
, column
);
1059 gtk_tree_view_set_reorderable (state
->treeview
,TRUE
);
1061 gtk_container_add (GTK_CONTAINER (scrolled
), GTK_WIDGET (state
->treeview
));
1062 gtk_widget_show (GTK_WIDGET (state
->treeview
));
1064 /* Set-up other widgets */
1065 state
->cell_sort_row_rb
= go_gtk_builder_get_widget (state
->gui
, "cell_sort_row_rb");
1066 state
->cell_sort_col_rb
= go_gtk_builder_get_widget (state
->gui
, "cell_sort_col_rb");
1067 g_signal_connect_swapped (G_OBJECT (state
->cell_sort_row_rb
),
1069 G_CALLBACK (cb_update_to_new_range
), state
);
1071 state
->cell_sort_header_check
= go_gtk_builder_get_widget (state
->gui
,
1072 "cell_sort_header_check");
1073 g_signal_connect_swapped (G_OBJECT (state
->cell_sort_header_check
),
1075 G_CALLBACK (cb_sort_header_check
), state
);
1077 state
->retain_format_check
= go_gtk_builder_get_widget (state
->gui
,
1078 "retain_format_button");
1079 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state
->retain_format_check
),
1080 gnm_conf_get_core_sort_default_retain_formats ());
1083 /* Set-up buttons */
1084 state
->up_button
= go_gtk_builder_get_widget (state
->gui
, "up_button");
1085 g_signal_connect_swapped (G_OBJECT (state
->up_button
),
1087 G_CALLBACK (cb_up
), state
);
1088 state
->down_button
= go_gtk_builder_get_widget (state
->gui
, "down_button");
1089 g_signal_connect_swapped (G_OBJECT (state
->down_button
),
1091 G_CALLBACK (cb_down
), state
);
1092 state
->add_button
= go_gtk_builder_get_widget (state
->gui
, "add_button");
1093 g_signal_connect_swapped (G_OBJECT (state
->add_button
),
1095 G_CALLBACK (cb_add_clicked
), state
);
1096 gtk_widget_set_sensitive (state
->add_button
, TRUE
);
1097 state
->delete_button
= go_gtk_builder_get_widget (state
->gui
, "delete_button");
1098 g_signal_connect (G_OBJECT (state
->delete_button
),
1100 G_CALLBACK (cb_delete_clicked
), state
);
1101 gtk_widget_set_sensitive (state
->delete_button
, FALSE
);
1103 state
->clear_button
= go_gtk_builder_get_widget (state
->gui
, "clear_button");
1104 g_signal_connect_swapped (G_OBJECT (state
->clear_button
),
1106 G_CALLBACK (cb_clear_clicked
), state
);
1107 gtk_widget_set_sensitive (state
->clear_button
, FALSE
);
1109 gtk_button_set_alignment (GTK_BUTTON (state
->up_button
), 0., .5);
1110 gtk_button_set_alignment (GTK_BUTTON (state
->down_button
), 0., .5);
1111 gtk_button_set_alignment (GTK_BUTTON (state
->add_button
), 0., .5);
1112 gtk_button_set_alignment (GTK_BUTTON (state
->delete_button
), 0., .5);
1113 gtk_button_set_alignment (GTK_BUTTON (state
->clear_button
), 0., .5);
1114 gnm_init_help_button (
1115 go_gtk_builder_get_widget (state
->gui
, "help_button"),
1116 GNUMERIC_HELP_LINK_CELL_SORT
);
1118 state
->ok_button
= go_gtk_builder_get_widget (state
->gui
, "ok_button");
1119 g_signal_connect_swapped (G_OBJECT (state
->ok_button
),
1121 G_CALLBACK (cb_dialog_ok_clicked
), state
);
1122 state
->cancel_button
= go_gtk_builder_get_widget (state
->gui
, "cancel_button");
1123 g_signal_connect (G_OBJECT (state
->cancel_button
),
1125 G_CALLBACK (cb_dialog_cancel_clicked
), state
);
1127 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
1129 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
1131 /* Finish dialog signals */
1132 wbc_gtk_attach_guru (state
->wbcg
, state
->dialog
);
1133 g_object_set_data_full (G_OBJECT (state
->dialog
),
1134 "state", state
, (GDestroyNotify
) cb_dialog_destroy
);
1136 dialog_load_selection (state
, &col_rb
);
1138 cb_sort_selection_changed (state
);
1139 gnm_expr_entry_grab_focus(GNM_EXPR_ENTRY (state
->add_entry
), TRUE
);
1143 * Main entry point for the Cell Sort dialog box
1146 dialog_cell_sort (WBCGtk
*wbcg
)
1148 SortFlowState
*state
;
1151 g_return_if_fail (wbcg
!= NULL
);
1153 if (gnm_dialog_raise_if_exists (wbcg
, CELL_SORT_KEY
))
1156 gui
= gnm_gtk_builder_load ("res:ui/cell-sort.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
1160 state
= g_new (SortFlowState
, 1);
1162 state
->wb
= wb_control_get_workbook (GNM_WBC (wbcg
));
1163 state
->sv
= wb_control_cur_sheet_view (GNM_WBC (wbcg
));
1164 state
->sheet
= sv_sheet (state
->sv
);
1165 state
->warning_dialog
= NULL
;
1167 state
->sort_items
= 0;
1169 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "CellSort");
1171 state
->image_ascending
=
1172 go_gtk_widget_render_icon_pixbuf (state
->dialog
,
1173 "view-sort-ascending",
1174 GTK_ICON_SIZE_LARGE_TOOLBAR
);
1175 state
->image_descending
=
1176 go_gtk_widget_render_icon_pixbuf (state
->dialog
,
1177 "view-sort-descending",
1178 GTK_ICON_SIZE_LARGE_TOOLBAR
);
1179 dialog_init (state
);
1181 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
1184 gtk_widget_show (state
->dialog
);