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>
27 #include <dialogs/dialogs.h>
28 #include <dialogs/help.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/gnm-cell-renderer-toggle.h>
45 #include <widgets/gnm-expr-entry.h>
48 #include <gsf/gsf-impl-utils.h>
49 #include <gdk/gdkkeysyms.h>
50 #include <goffice/goffice.h>
52 #define CELL_SORT_KEY "cell-sort-dialog"
64 GtkWidget
*warning_dialog
;
65 GtkWidget
*cancel_button
;
68 GtkWidget
*down_button
;
69 GtkWidget
*add_button
;
70 GtkWidget
*delete_button
;
71 GtkWidget
*clear_button
;
72 GnmExprEntry
*range_entry
;
73 GnmExprEntry
*add_entry
;
75 GtkTreeView
*treeview
;
76 GtkTreeViewColumn
*header_column
;
77 GtkTreeSelection
*selection
;
78 GtkWidget
*cell_sort_row_rb
;
79 GtkWidget
*cell_sort_col_rb
;
80 GtkWidget
*cell_sort_header_check
;
81 GtkWidget
*retain_format_check
;
82 GdkPixbuf
*image_ascending
;
83 GdkPixbuf
*image_descending
;
84 GOLocaleSel
*locale_selector
;
97 ITEM_DESCENDING_IMAGE
,
105 static const gint MAX_MENU_SIZE
= 20;
110 gboolean done_submenu
;
111 SortFlowState
*state
;
112 } AddSortFieldMenuState
;
115 header_name (Sheet
*sheet
, int col
, int row
)
120 cell
= sheet_cell_get (sheet
, col
, row
);
122 str
= value_get_as_string (cell
->value
);
129 col_row_name (Sheet
*sheet
, int col
, int row
, gboolean header
, gboolean is_cols
)
135 str
= g_strdup_printf (_("Column %s"), col_name (col
));
137 str
= g_strdup_printf (_("Row %s"), row_name (row
));
140 cell
= sheet_cell_get (sheet
, col
, row
);
141 if (cell
&& !gnm_cell_is_blank (cell
)) {
142 gchar
*header_str
, *generic_str
= str
;
143 header_str
= value_get_as_string (cell
->value
);
144 str
= g_strdup_printf (_("%s (%s)"), header_str
, generic_str
);
146 g_free (generic_str
);
155 already_in_sort_fields(int index
, SortFlowState
*state
)
161 /* See if index is already in the sort fields */
162 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state
->model
),
163 &iter
, NULL
, item
)) {
164 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
165 ITEM_NUMBER
, &number
,
169 if (number
== index
) {
174 /* Here means not already in sort fields */
179 range_already_in_sort_criteria(gint start
, gint end
, SortFlowState
*state
)
182 for (i
=start
; i
<=end
; i
++) {
183 if (!already_in_sort_fields(i
, state
))
191 build_sort_field_menu (gint start
, gint end
, gint index
, GtkWidget
*menu
, SortFlowState
*state
, int used
);
194 cb_sort_field_menu_activate(GtkWidget
*item
, AddSortFieldMenuState
*menu_state
)
196 GtkWidget
*menu
= GTK_WIDGET (gtk_menu_item_get_submenu(GTK_MENU_ITEM (item
)));
198 if (menu_state
->done_submenu
== FALSE
) {
199 build_sort_field_menu(menu_state
->start
,
203 menu_state
->state
, 0);
204 menu_state
->done_submenu
= TRUE
;
209 set_button_sensitivity(SortFlowState
*state
)
213 if (state
->sel
== NULL
) {
214 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
218 items
= state
->is_cols
? (state
->sel
->v_range
.cell
.b
.row
-
219 state
->sel
->v_range
.cell
.a
.row
+ 1) :
220 (state
->sel
->v_range
.cell
.b
.col
-
221 state
->sel
->v_range
.cell
.a
.col
+ 1);
224 gtk_widget_set_sensitive (state
->ok_button
,
225 (state
->sort_items
!= 0) &&
227 gtk_widget_set_sensitive (state
->clear_button
, state
->sort_items
!= 0);
231 append_data (SortFlowState
*state
, int i
, int index
)
235 Sheet
*sheet
= state
->sel
->v_range
.cell
.a
.sheet
;
236 gboolean sort_asc
= gnm_conf_get_core_sort_default_ascending ();
238 header
= state
->is_cols
239 ? header_name (sheet
, i
, index
)
240 : header_name (sheet
, index
, i
);
242 ? col_row_name (sheet
, i
, index
, FALSE
, TRUE
)
243 : col_row_name (sheet
, index
, i
, FALSE
, FALSE
);
244 gtk_list_store_append (state
->model
, &iter
);
245 gtk_list_store_set (state
->model
, &iter
,
248 ITEM_DESCENDING
, !sort_asc
,
249 ITEM_DESCENDING_IMAGE
, sort_asc
? state
->image_ascending
250 : state
->image_descending
,
251 ITEM_CASE_SENSITIVE
, gnm_conf_get_core_sort_default_by_case (),
252 ITEM_SORT_BY_VALUE
, TRUE
,
253 ITEM_MOVE_FORMAT
, TRUE
,
262 cb_sort_field_selection(G_GNUC_UNUSED GtkWidget
*item
, AddSortFieldMenuState
*menu_state
)
264 append_data(menu_state
->state
,
267 /* Update sensitivity if this is the first sort item. */
268 if (menu_state
->state
->sort_items
== 1)
269 set_button_sensitivity(menu_state
->state
);
273 build_sort_field_menu (gint start
, gint end
, gint index
, GtkWidget
*menu
, SortFlowState
*state
, int used
)
275 Sheet
*sheet
= state
->sel
->v_range
.cell
.a
.sheet
;
283 AddSortFieldMenuState
*menu_state
;
286 menu_size
= 1 + end
- start
;
287 if (MAX_MENU_SIZE
< menu_size
- used
) {
289 gint balanced_submenu_size
;
291 submenu_size
= (menu_size
+ MAX_MENU_SIZE
- 1) / MAX_MENU_SIZE
;
292 balanced_submenu_size
= sqrt((double)
293 (menu_size
+ MAX_MENU_SIZE
- 1));
294 if (balanced_submenu_size
> submenu_size
)
295 submenu_size
= balanced_submenu_size
;
297 for (i
= start
; i
<= end
; i
+=submenu_size
) {
298 this_end
= i
+ submenu_size
- 1;
302 /* See if there are any fields in this range that aren't already
305 if (range_already_in_sort_criteria(i
, this_end
, state
))
308 str_start
= state
->is_cols
309 ? col_row_name (sheet
, i
, index
, state
->header
, TRUE
)
310 : col_row_name (sheet
, index
, i
, state
->header
, FALSE
);
312 str_end
= state
->is_cols
313 ? col_row_name (sheet
, this_end
, index
, state
->header
, TRUE
)
314 : col_row_name (sheet
, index
, this_end
, state
->header
, FALSE
);
316 str
= g_strdup_printf(_("%s to %s"), str_start
, str_end
);
320 item
= (GtkWidget
*) gtk_menu_item_new_with_label(str
);
321 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
322 gtk_widget_show (item
);
324 menu_state
= g_new(AddSortFieldMenuState
, 1);
325 menu_state
->start
= i
;
326 menu_state
->end
= this_end
;
327 menu_state
->index
= index
;
328 menu_state
->state
= state
;
329 menu_state
->done_submenu
= FALSE
;
330 submenu
= gtk_menu_new();
331 gtk_menu_item_set_submenu(GTK_MENU_ITEM (item
), submenu
);
332 g_signal_connect (item
, "activate",
333 G_CALLBACK (cb_sort_field_menu_activate
), menu_state
);
336 for (i
= start
; i
<= end
; i
++) {
337 if (FALSE
== already_in_sort_fields(i
, state
)) {
340 ? col_row_name (sheet
, i
, index
, state
->header
, TRUE
)
341 : col_row_name (sheet
, index
, i
, state
->header
, FALSE
);
342 item
= (GtkWidget
*) gtk_menu_item_new_with_label(str
);
343 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
344 gtk_widget_show (item
);
345 menu_state
= g_new(AddSortFieldMenuState
, 1);
346 menu_state
->start
= i
;
348 menu_state
->index
= index
;
349 menu_state
->state
= state
;
350 menu_state
->done_submenu
= FALSE
;
351 g_signal_connect (item
, "activate",
352 G_CALLBACK (cb_sort_field_selection
),
360 load_model_data (SortFlowState
*state
)
366 int limit
= gnm_conf_get_core_sort_dialog_max_initial_clauses ();
368 if (state
->is_cols
) {
369 start
= state
->sel
->v_range
.cell
.a
.col
;
370 end
= state
->sel
->v_range
.cell
.b
.col
;
371 index
= state
->sel
->v_range
.cell
.a
.row
;
373 start
= state
->sel
->v_range
.cell
.a
.row
;
374 end
= state
->sel
->v_range
.cell
.b
.row
;
375 index
= state
->sel
->v_range
.cell
.a
.col
;
378 gtk_list_store_clear (state
->model
);
379 state
->sort_items
= 0;
381 if (end
>= start
+ limit
)
382 end
= start
+ limit
- 1;
384 for (i
= start
; i
<= end
; i
++)
385 append_data (state
, i
, index
);
389 translate_range (GnmValue
*range
, SortFlowState
*state
)
391 state
->is_cols
= !gtk_toggle_button_get_active (
392 GTK_TOGGLE_BUTTON (state
->cell_sort_row_rb
));
393 state
->header
= gtk_toggle_button_get_active (
394 GTK_TOGGLE_BUTTON (state
->cell_sort_header_check
));
396 value_release (state
->sel
);
398 load_model_data(state
);
402 cb_sort_header_check(SortFlowState
*state
)
404 state
->header
= gtk_toggle_button_get_active (
405 GTK_TOGGLE_BUTTON (state
->cell_sort_header_check
));
407 gtk_tree_view_column_set_visible (state
->header_column
, state
->header
);
408 set_button_sensitivity (state
);
412 cb_update_to_new_range (SortFlowState
*state
)
416 range
= gnm_expr_entry_parse_as_value
417 (GNM_EXPR_ENTRY (state
->range_entry
), state
->sheet
);
419 if (state
->sel
!= NULL
) {
420 value_release (state
->sel
);
422 gtk_list_store_clear (state
->model
);
423 state
->sort_items
= 0;
426 translate_range (range
, state
);
427 set_button_sensitivity (state
);
431 cb_dialog_destroy (SortFlowState
*state
)
433 value_release (state
->sel
);
436 g_clear_object (&state
->model
);
437 g_clear_object (&state
->gui
);
439 wbcg_edit_finish (state
->wbcg
, WBC_EDIT_REJECT
, NULL
);
441 state
->dialog
= NULL
;
443 g_clear_object (&state
->image_ascending
);
444 g_clear_object (&state
->image_descending
);
450 cb_dialog_ok_clicked (SortFlowState
*state
)
452 GnmSortData
*data
, *data_copy
;
453 GnmSortClause
*array
, *this_array_item
;
456 gboolean descending
, case_sensitive
, sort_by_value
, move_format
;
461 array
= g_new (GnmSortClause
, state
->sort_items
);
462 this_array_item
= array
;
463 base
= (state
->is_cols
? state
->sel
->v_range
.cell
.a
.col
: state
->sel
->v_range
.cell
.a
.row
);
465 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state
->model
),
466 &iter
, NULL
, item
)) {
467 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
468 ITEM_DESCENDING
,&descending
,
469 ITEM_CASE_SENSITIVE
, &case_sensitive
,
470 ITEM_SORT_BY_VALUE
, &sort_by_value
,
471 ITEM_MOVE_FORMAT
, &move_format
,
472 ITEM_NUMBER
, &number
,
475 this_array_item
->offset
= number
- base
;
476 this_array_item
->asc
= !!descending
;
477 this_array_item
->cs
= case_sensitive
;
478 this_array_item
->val
= sort_by_value
;
483 data
= g_new (GnmSortData
, 1);
484 data
->sheet
= state
->sel
->v_range
.cell
.a
.sheet
;
485 data
->range
= g_new (GnmRange
, 1);
486 data
->range
= range_init (data
->range
, state
->sel
->v_range
.cell
.a
.col
487 + ((state
->header
&& !state
->is_cols
) ? 1 : 0),
488 state
->sel
->v_range
.cell
.a
.row
489 + ((state
->header
&& state
->is_cols
) ? 1 : 0),
490 state
->sel
->v_range
.cell
.b
.col
,
491 state
->sel
->v_range
.cell
.b
.row
);
492 data
->num_clause
= state
->sort_items
;
493 data
->clauses
= array
;
494 data
->top
= state
->is_cols
;
495 data
->retain_formats
= gtk_toggle_button_get_active (
496 GTK_TOGGLE_BUTTON (state
->retain_format_check
));
497 data
->locale
= go_locale_sel_get_locale (state
->locale_selector
);
499 data_copy
= gnm_sort_data_copy (data
);
500 text
= gnm_expr_entry_get_text (state
->range_entry
);
501 gnm_sheet_add_sort_setup
503 g_strdup((text
!= NULL
&& text
[0] != '\0') ? text
: "Other"),
506 cmd_sort (GNM_WBC (state
->wbcg
), data
);
508 gtk_widget_destroy (state
->dialog
);
513 cb_dialog_cancel_clicked (G_GNUC_UNUSED GtkWidget
*button
,
514 SortFlowState
*state
)
516 gtk_widget_destroy (state
->dialog
);
520 dialog_cell_sort_load_sort_setup (SortFlowState
*state
, GnmSortData
const *data
)
523 GnmSortClause
*this = data
->clauses
;
524 gint base
, max
, index
;
525 Sheet
*sheet
= state
->sel
->v_range
.cell
.a
.sheet
;
528 sheet
= state
->sheet
;
530 go_locale_sel_set_locale (state
->locale_selector
, data
->locale
);
532 gtk_toggle_button_set_active (
533 GTK_TOGGLE_BUTTON (state
->retain_format_check
), data
->retain_formats
);
535 gtk_toggle_button_set_active (
536 GTK_TOGGLE_BUTTON (state
->cell_sort_row_rb
), !data
->top
);
537 state
->is_cols
= data
->top
;
539 index
= (data
->top
? state
->sel
->v_range
.cell
.a
.row
: state
->sel
->v_range
.cell
.a
.col
);
540 base
= (data
->top
? state
->sel
->v_range
.cell
.a
.col
: state
->sel
->v_range
.cell
.a
.row
);
541 max
= (data
->top
? state
->sel
->v_range
.cell
.b
.col
: state
->sel
->v_range
.cell
.b
.row
);
542 gtk_list_store_clear (state
->model
);
543 state
->sort_items
= 0;
544 for (i
= 0; i
< data
->num_clause
; i
++) {
545 if (data
->clauses
[i
].offset
<= max
) {
548 int id
= data
->clauses
[i
].offset
+ base
;
550 header
= state
->is_cols
551 ? header_name (sheet
, id
, index
)
552 : header_name (sheet
, index
, id
);
553 str
= col_row_name (sheet
, id
, id
, FALSE
, state
->is_cols
);
555 gtk_list_store_append (state
->model
, &iter
);
556 gtk_list_store_set (state
->model
, &iter
,
559 ITEM_DESCENDING
, data
->clauses
[i
].asc
,
560 ITEM_DESCENDING_IMAGE
,
561 !data
->clauses
[i
].asc
562 ? state
->image_ascending
563 : state
->image_descending
,
564 ITEM_CASE_SENSITIVE
, data
->clauses
[i
].cs
,
565 ITEM_SORT_BY_VALUE
, data
->clauses
[i
].val
,
566 ITEM_MOVE_FORMAT
, TRUE
,
573 set_button_sensitivity (state
);
576 static GnmRange
const *
577 dialog_load_selection (SortFlowState
*state
, gboolean
*col_rb
)
579 GnmRange
const *first
;
580 GnmSortData
const *data
;
582 first
= selection_first_range (state
->sv
, NULL
, NULL
);
585 gtk_toggle_button_set_active (
586 GTK_TOGGLE_BUTTON (state
->cell_sort_col_rb
),
587 (*col_rb
= (first
->end
.row
- first
->start
.row
> first
->end
.col
- first
->start
.col
)));
588 gnm_expr_entry_load_from_range (state
->range_entry
,
589 state
->sheet
, first
);
591 gtk_toggle_button_set_active (
592 GTK_TOGGLE_BUTTON (state
->cell_sort_col_rb
),
595 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state
->cell_sort_header_check
),
596 sheet_range_has_heading
597 (state
->sheet
, first
, *col_rb
, FALSE
));
598 cb_sort_header_check (state
);
600 data
= gnm_sheet_find_sort_setup (state
->sheet
,
601 gnm_expr_entry_get_text (state
->range_entry
));
603 dialog_cell_sort_load_sort_setup (state
, data
);
605 cb_update_to_new_range (state
);
611 * cb_sort_selection_changed:
613 * 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
= gnm_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
);