2 * dialog-stf-fixed-page.c : Controls the widgets on the fixed page of the dialog (fixed-width page that is)
4 * Copyright 2001 Almer S. Tigelaar <almer@gnome.org>
5 * Copyright 2003 Morten Welinder <terra@gnome.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
21 #include <gnumeric-config.h>
22 #include <glib/gi18n-lib.h>
24 #include "dialog-stf.h"
26 #include <gdk/gdkkeysyms.h>
29 /*************************************************************************************************
30 * MISC UTILITY FUNCTIONS
31 *************************************************************************************************/
34 * fixed_page_autodiscover:
35 * @pagedata: a mother struct
37 * Use the STF's autodiscovery function and put the
38 * result in the fixed_collist
41 fixed_page_autodiscover (StfDialogData
*pagedata
)
43 stf_parse_options_fixed_autodiscover (pagedata
->parseoptions
,
44 pagedata
->cur
, pagedata
->cur_end
);
46 if (pagedata
->parseoptions
->splitpositions
->len
<= 1) {
47 GtkWidget
*dialog
= gtk_message_dialog_new (NULL
,
48 GTK_DIALOG_DESTROY_WITH_PARENT
,
51 _("Autodiscovery did not find any columns in the text. Try manually"));
52 go_gtk_dialog_run (GTK_DIALOG (dialog
), GTK_WINDOW (pagedata
->dialog
));
56 static void fixed_page_update_preview (StfDialogData
*pagedata
);
59 CONTEXT_STF_IMPORT_MERGE_LEFT
= 1,
60 CONTEXT_STF_IMPORT_MERGE_RIGHT
= 2,
61 CONTEXT_STF_IMPORT_SPLIT
= 3,
62 CONTEXT_STF_IMPORT_WIDEN
= 4,
63 CONTEXT_STF_IMPORT_NARROW
= 5
66 static GnmPopupMenuElement
const popup_elements
[] = {
67 { N_("Merge with column on _left"), GTK_STOCK_REMOVE
,
68 0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT
, CONTEXT_STF_IMPORT_MERGE_LEFT
},
69 { N_("Merge with column on _right"), GTK_STOCK_REMOVE
,
70 0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT
, CONTEXT_STF_IMPORT_MERGE_RIGHT
},
71 { "", NULL
, 0, 0, 0 },
72 { N_("_Split this column"), NULL
,
73 0, 1 << CONTEXT_STF_IMPORT_SPLIT
, CONTEXT_STF_IMPORT_SPLIT
},
74 { "", NULL
, 0, 0, 0 },
75 { N_("_Widen this column"), GTK_STOCK_GO_FORWARD
,
76 0, 1 << CONTEXT_STF_IMPORT_WIDEN
, CONTEXT_STF_IMPORT_WIDEN
},
77 { N_("_Narrow this column"), GTK_STOCK_GO_BACK
,
78 0, 1 << CONTEXT_STF_IMPORT_NARROW
, CONTEXT_STF_IMPORT_NARROW
},
79 { NULL
, NULL
, 0, 0, 0 },
83 calc_char_index (RenderData_t
*renderdata
, int col
, int *dx
)
85 GtkCellRenderer
*cell
= stf_preview_get_cell_renderer (renderdata
, col
);
87 PangoFontDescription
*font_desc
;
90 gtk_cell_renderer_get_padding (cell
, &padx
, NULL
);
92 g_object_get (G_OBJECT (cell
), "font_desc", &font_desc
, NULL
);
93 layout
= gtk_widget_create_pango_layout (GTK_WIDGET (renderdata
->tree_view
), "x");
94 pango_layout_set_font_description (layout
, font_desc
);
95 pango_layout_get_pixel_size (layout
, &width
, NULL
);
96 g_object_unref (layout
);
97 pango_font_description_free (font_desc
);
99 if (width
< 1) width
= 1;
100 ci
= (*dx
< padx
) ? 0 : (*dx
- padx
+ width
/ 2) / width
;
108 make_new_column (StfDialogData
*pagedata
, int col
, int dx
, gboolean test_only
)
111 RenderData_t
*renderdata
= pagedata
->fixed
.renderdata
;
112 int colstart
, colend
;
114 colstart
= (col
== 0)
116 : stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
- 1);
117 colend
= stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
);
119 charindex
= colstart
+ calc_char_index (renderdata
, col
, &dx
);
121 if (charindex
<= colstart
|| (colend
!= -1 && charindex
>= colend
))
125 stf_parse_options_fixed_splitpositions_add (pagedata
->parseoptions
, charindex
);
126 fixed_page_update_preview (pagedata
);
134 widen_column (StfDialogData
*pagedata
, int col
, gboolean test_only
)
136 int colcount
= stf_parse_options_fixed_splitpositions_count (pagedata
->parseoptions
);
137 int nextstart
, nextnextstart
;
139 if (col
>= colcount
- 1)
142 nextstart
= stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
);
144 nextnextstart
= (col
== colcount
- 2)
145 ? pagedata
->longest_line
146 : stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
+ 1);
148 if (nextstart
+ 1 >= nextnextstart
)
152 stf_parse_options_fixed_splitpositions_remove (pagedata
->parseoptions
, nextstart
);
153 stf_parse_options_fixed_splitpositions_add (pagedata
->parseoptions
, nextstart
+ 1);
154 fixed_page_update_preview (pagedata
);
160 narrow_column (StfDialogData
*pagedata
, int col
, gboolean test_only
)
162 int colcount
= stf_parse_options_fixed_splitpositions_count (pagedata
->parseoptions
);
163 int thisstart
, nextstart
;
165 if (col
>= colcount
- 1)
168 thisstart
= (col
== 0)
170 : stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
- 1);
171 nextstart
= stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
);
173 if (nextstart
- 1 <= thisstart
)
177 stf_parse_options_fixed_splitpositions_remove (pagedata
->parseoptions
, nextstart
);
178 stf_parse_options_fixed_splitpositions_add (pagedata
->parseoptions
, nextstart
- 1);
179 fixed_page_update_preview (pagedata
);
185 delete_column (StfDialogData
*pagedata
, int col
, gboolean test_only
)
187 int colcount
= stf_parse_options_fixed_splitpositions_count (pagedata
->parseoptions
);
188 if (col
< 0 || col
>= colcount
- 1)
192 int nextstart
= stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
);
193 stf_parse_options_fixed_splitpositions_remove (pagedata
->parseoptions
, nextstart
);
194 fixed_page_update_preview (pagedata
);
200 select_column (StfDialogData
*pagedata
, int col
)
202 int colcount
= stf_parse_options_fixed_splitpositions_count (pagedata
->parseoptions
);
203 GtkTreeViewColumn
*column
;
205 if (col
< 0 || col
>= colcount
)
208 column
= stf_preview_get_column (pagedata
->fixed
.renderdata
, col
);
209 gtk_widget_grab_focus (gtk_tree_view_column_get_button (column
));
213 fixed_context_menu_handler (GnmPopupMenuElement
const *element
,
216 StfDialogData
*pagedata
= user_data
;
217 int col
= pagedata
->fixed
.context_col
;
219 switch (element
->index
) {
220 case CONTEXT_STF_IMPORT_MERGE_LEFT
:
221 delete_column (pagedata
, col
- 1, FALSE
);
223 case CONTEXT_STF_IMPORT_MERGE_RIGHT
:
224 delete_column (pagedata
, col
, FALSE
);
226 case CONTEXT_STF_IMPORT_SPLIT
:
227 make_new_column (pagedata
, col
, pagedata
->fixed
.context_dx
, FALSE
);
229 case CONTEXT_STF_IMPORT_WIDEN
:
230 widen_column (pagedata
, col
, FALSE
);
232 case CONTEXT_STF_IMPORT_NARROW
:
233 narrow_column (pagedata
, col
, FALSE
);
241 fixed_context_menu (StfDialogData
*pagedata
, GdkEventButton
*event
,
244 int sensitivity_filter
= 0;
246 pagedata
->fixed
.context_col
= col
;
247 pagedata
->fixed
.context_dx
= dx
;
249 if (!delete_column (pagedata
, col
- 1, TRUE
))
250 sensitivity_filter
|= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT
);
251 if (!delete_column (pagedata
, col
, TRUE
))
252 sensitivity_filter
|= (1 << CONTEXT_STF_IMPORT_MERGE_RIGHT
);
253 if (!make_new_column (pagedata
, col
, dx
, TRUE
))
254 sensitivity_filter
|= (1 << CONTEXT_STF_IMPORT_SPLIT
);
255 if (!widen_column (pagedata
, col
, TRUE
))
256 sensitivity_filter
|= (1 << CONTEXT_STF_IMPORT_WIDEN
);
257 if (!narrow_column (pagedata
, col
, TRUE
))
258 sensitivity_filter
|= (1 << CONTEXT_STF_IMPORT_NARROW
);
260 select_column (pagedata
, col
);
261 gnm_create_popup_menu (popup_elements
, &fixed_context_menu_handler
,
268 cb_treeview_button_press (GtkWidget
*treeview
,
269 GdkEventButton
*event
,
270 StfDialogData
*pagedata
)
272 if (event
->type
== GDK_2BUTTON_PRESS
&& event
->button
== 1) {
274 stf_preview_find_column (pagedata
->fixed
.renderdata
, (int)event
->x
, &col
, &dx
);
275 make_new_column (pagedata
, col
, dx
, FALSE
);
279 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3) {
281 stf_preview_find_column (pagedata
->fixed
.renderdata
, (int)event
->x
, &col
, &dx
);
282 fixed_context_menu (pagedata
, event
, col
, dx
);
291 cb_col_button_press (GtkWidget
*button
,
292 GdkEventButton
*event
,
295 int col
= GPOINTER_TO_INT (_col
);
296 StfDialogData
*data
= g_object_get_data (G_OBJECT (button
), "fixed-data");
298 if (event
->type
== GDK_2BUTTON_PRESS
&& event
->button
== 1) {
299 GtkAllocation bca
, ba
;
303 /* Correct for indentation of button. */
304 gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (button
)),
306 gtk_widget_get_allocation (button
, &ba
);
307 offset
= bca
.x
- ba
.x
;
308 make_new_column (data
, col
, (int)event
->x
- offset
, FALSE
);
313 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3) {
314 GtkAllocation bca
, ba
;
317 /* Correct for indentation of button. */
318 gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (button
)),
320 gtk_widget_get_allocation (button
, &ba
);
321 offset
= bca
.x
- ba
.x
;
323 fixed_context_menu (data
, event
, col
, (int)event
->x
- offset
);
331 cb_col_key_press (GtkWidget
*button
,
335 int col
= GPOINTER_TO_INT (_col
);
336 StfDialogData
*data
= g_object_get_data (G_OBJECT (button
), "fixed-data");
338 if (event
->type
== GDK_KEY_PRESS
) {
339 switch (event
->keyval
) {
342 case GDK_KEY_greater
:
343 widen_column (data
, col
, FALSE
);
347 case GDK_KEY_KP_Subtract
:
349 narrow_column (data
, col
, FALSE
);
354 select_column (data
, col
- 1);
359 select_column (data
, col
+ 1);
371 * fixed_page_update_preview
372 * @pagedata: mother struct
374 * Will simply update the preview
379 fixed_page_update_preview (StfDialogData
*pagedata
)
381 StfParseOptions_t
*parseoptions
= pagedata
->parseoptions
;
382 RenderData_t
*renderdata
= pagedata
->fixed
.renderdata
;
384 GStringChunk
*lines_chunk
;
388 lines_chunk
= g_string_chunk_new (100 * 1024);
390 /* Don't trim on this page. */
391 trim
= parseoptions
->trim_spaces
;
392 stf_parse_options_set_trim_spaces (parseoptions
, TRIM_TYPE_NEVER
);
393 lines
= stf_parse_general (parseoptions
, lines_chunk
,
394 pagedata
->cur
, pagedata
->cur_end
);
395 stf_parse_options_set_trim_spaces (parseoptions
, trim
);
397 stf_preview_set_lines (renderdata
, lines_chunk
, lines
);
399 for (i
= 0; i
< renderdata
->colcount
; i
++) {
400 GtkTreeViewColumn
*column
=
401 stf_preview_get_column (renderdata
, i
);
402 GtkCellRenderer
*cell
=
403 stf_preview_get_cell_renderer (renderdata
, i
);
405 gtk_tree_view_column_get_button (column
);
407 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
409 g_object_set (G_OBJECT (cell
),
410 "family", "monospace",
413 g_object_set_data (G_OBJECT (button
), "fixed-data", pagedata
);
414 g_object_set (G_OBJECT (column
), "clickable", TRUE
, NULL
);
415 g_signal_connect (button
, "button_press_event",
416 G_CALLBACK (cb_col_button_press
),
417 GINT_TO_POINTER (i
));
418 g_signal_connect (button
, "key_press_event",
419 G_CALLBACK (cb_col_key_press
),
420 GINT_TO_POINTER (i
));
424 /*************************************************************************************************
426 *************************************************************************************************/
429 * fixed_page_clear_clicked:
431 * @data: mother struct
433 * Will clear all entries in fixed_collist
436 fixed_page_clear_clicked (G_GNUC_UNUSED GtkButton
*button
,
439 stf_parse_options_fixed_splitpositions_clear (data
->parseoptions
);
440 fixed_page_update_preview (data
);
444 * fixed_page_auto_clicked:
446 * @data: mother struct
448 * Will try to automatically recognize columns in the
452 fixed_page_auto_clicked (G_GNUC_UNUSED GtkButton
*button
,
455 fixed_page_autodiscover (data
);
457 fixed_page_update_preview (data
);
461 * stf_dialog_fixed_page_prepare
462 * @pagedata: mother struct
464 * Will prepare the fixed page
467 stf_dialog_fixed_page_prepare (StfDialogData
*pagedata
)
469 stf_parse_options_set_trim_spaces (pagedata
->parseoptions
, TRIM_TYPE_NEVER
);
470 fixed_page_update_preview (pagedata
);
474 queue_redraw (GtkWidget
*widget
, int x
)
482 gtk_tree_view_convert_bin_window_to_widget_coords
483 (GTK_TREE_VIEW (widget
), 0, 0, &xo
, &hh
);
485 gtk_widget_get_allocation (widget
, &a
);
486 gtk_widget_queue_draw_area (widget
,
493 cb_treeview_motion (GtkWidget
*widget
,
494 GdkEventMotion
*event
,
495 StfDialogData
*pagedata
)
497 int x
= (int)event
->x
;
499 RenderData_t
*renderdata
= pagedata
->fixed
.renderdata
;
500 int old_ruler_x
= pagedata
->fixed
.ruler_x
;
501 int colstart
, colend
, colwidth
;
504 pagedata
->fixed
.ruler_x
= -1;
506 /* We get events from the buttons too. Translate x. */
507 gdk_window_get_user_data (event
->window
, &user
);
508 if (GTK_IS_BUTTON (user
)) {
510 gdk_window_get_position (event
->window
, &ewx
, NULL
);
514 stf_preview_find_column (renderdata
, x
, &col
, &dx
);
516 colstart
= (col
== 0)
518 : stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
- 1);
519 colend
= stf_parse_options_fixed_splitpositions_nth (pagedata
->parseoptions
, col
);
520 colwidth
= (colend
== -1) ? G_MAXINT
: colend
- colstart
;
522 if (col
>= 0 && col
< renderdata
->colcount
) {
523 int ci
= calc_char_index (renderdata
, col
, &dx
);
524 if (ci
<= colwidth
) {
526 GtkCellRenderer
*cell
=
527 stf_preview_get_cell_renderer (renderdata
, col
);
529 gtk_cell_renderer_get_padding (cell
, &padx
, NULL
);
530 pagedata
->fixed
.ruler_x
= x
- dx
+ padx
;
534 gdk_event_request_motions (event
);
536 if (pagedata
->fixed
.ruler_x
== old_ruler_x
)
539 queue_redraw (widget
, old_ruler_x
);
540 queue_redraw (widget
, pagedata
->fixed
.ruler_x
);
547 cb_treeview_draw (GtkWidget
*widget
,
549 StfDialogData
*pagedata
)
551 int ruler_x
= pagedata
->fixed
.ruler_x
;
554 GdkWindow
*bin_window
;
556 GtkStyleContext
*context
;
561 bin_window
= gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget
));
562 if (!gtk_cairo_should_draw_window (cr
, bin_window
))
565 gtk_widget_get_allocation (widget
, &a
);
568 context
= gtk_widget_get_style_context (GTK_WIDGET (pagedata
->dialog
));
569 gtk_style_context_save (context
);
570 gtk_style_context_add_region (context
, "fixed-format-ruler", 0);
571 gnm_style_context_get_color (context
, GTK_STATE_FLAG_NORMAL
,
573 gtk_style_context_restore (context
);
576 cairo_rectangle (cr
, ruler_x
, 0, ruler_x
+ 1, height
);
578 gdk_cairo_set_source_rgba (cr
, &ruler_color
);
579 cairo_move_to (cr
, ruler_x
, 0);
580 cairo_line_to (cr
, ruler_x
, height
);
587 /*************************************************************************************************
588 * FIXED EXPORTED FUNCTIONS
589 *************************************************************************************************/
592 * stf_dialog_fixed_page_cleanup
593 * @pagedata: mother struct
595 * Will cleanup fixed page run-time data
600 stf_dialog_fixed_page_cleanup (StfDialogData
*pagedata
)
602 stf_preview_free (pagedata
->fixed
.renderdata
);
606 stf_dialog_fixed_page_init (GtkBuilder
*gui
, StfDialogData
*pagedata
)
608 RenderData_t
*renderdata
;
610 g_return_if_fail (gui
!= NULL
);
611 g_return_if_fail (pagedata
!= NULL
);
613 /* Create/get object and fill information struct */
614 pagedata
->fixed
.fixed_clear
= GTK_BUTTON (go_gtk_builder_get_widget (gui
, "fixed_clear"));
615 pagedata
->fixed
.fixed_auto
= GTK_BUTTON (go_gtk_builder_get_widget (gui
, "fixed_auto"));
616 pagedata
->fixed
.fixed_data_container
= (go_gtk_builder_get_widget (gui
, "fixed_data_container"));
619 renderdata
= pagedata
->fixed
.renderdata
=
620 stf_preview_new (pagedata
->fixed
.fixed_data_container
,
622 pagedata
->fixed
.ruler_x
= -1;
624 /* Connect signals */
625 g_signal_connect (G_OBJECT (pagedata
->fixed
.fixed_clear
),
627 G_CALLBACK (fixed_page_clear_clicked
), pagedata
);
628 g_signal_connect (G_OBJECT (pagedata
->fixed
.fixed_auto
),
630 G_CALLBACK (fixed_page_auto_clicked
), pagedata
);
631 g_signal_connect (G_OBJECT (renderdata
->tree_view
),
632 "button_press_event",
633 G_CALLBACK (cb_treeview_button_press
), pagedata
);
634 g_signal_connect (G_OBJECT (renderdata
->tree_view
),
635 "motion_notify_event",
636 G_CALLBACK (cb_treeview_motion
), pagedata
);
638 g_signal_connect_after (G_OBJECT (renderdata
->tree_view
),
640 G_CALLBACK (cb_treeview_draw
), pagedata
);