Compilation: don't compile dialogs separately.
[gnumeric.git] / src / dialogs / dialog-stf-fixed-page.c
blob128f14c97260bc3cf60a87654b51aeb103d351c0
1 /*
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>
23 #include <gnumeric.h>
24 #include "dialog-stf.h"
25 #include <gui-util.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <gtk/gtk.h>
29 /*************************************************************************************************
30 * MISC UTILITY FUNCTIONS
31 *************************************************************************************************/
33 /**
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
39 **/
40 static void
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,
49 GTK_MESSAGE_INFO,
50 GTK_BUTTONS_OK,
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);
58 enum {
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 },
82 static int
83 calc_char_index (RenderData_t *renderdata, int col, int *dx)
85 GtkCellRenderer *cell = stf_preview_get_cell_renderer (renderdata, col);
86 PangoLayout *layout;
87 PangoFontDescription *font_desc;
88 int ci, width, padx;
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;
101 *dx -= ci * width;
103 return ci;
107 static gboolean
108 make_new_column (StfDialogData *pagedata, int col, int dx, gboolean test_only)
110 int charindex;
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))
122 return FALSE;
124 if (!test_only) {
125 stf_parse_options_fixed_splitpositions_add (pagedata->parseoptions, charindex);
126 fixed_page_update_preview (pagedata);
129 return TRUE;
133 static gboolean
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)
140 return FALSE;
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)
149 return FALSE;
151 if (!test_only) {
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);
156 return TRUE;
159 static gboolean
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)
166 return FALSE;
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)
174 return FALSE;
176 if (!test_only) {
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);
181 return TRUE;
184 static gboolean
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)
189 return FALSE;
191 if (!test_only) {
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);
196 return TRUE;
199 static void
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)
206 return;
208 column = stf_preview_get_column (pagedata->fixed.renderdata, col);
209 gtk_widget_grab_focus (gtk_tree_view_column_get_button (column));
212 static void
213 fixed_context_menu_handler (GnmPopupMenuElement const *element,
214 gpointer user_data)
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);
222 break;
223 case CONTEXT_STF_IMPORT_MERGE_RIGHT:
224 delete_column (pagedata, col, FALSE);
225 break;
226 case CONTEXT_STF_IMPORT_SPLIT:
227 make_new_column (pagedata, col, pagedata->fixed.context_dx, FALSE);
228 break;
229 case CONTEXT_STF_IMPORT_WIDEN:
230 widen_column (pagedata, col, FALSE);
231 break;
232 case CONTEXT_STF_IMPORT_NARROW:
233 narrow_column (pagedata, col, FALSE);
234 break;
235 default:
236 ; /* Nothing */
240 static void
241 fixed_context_menu (StfDialogData *pagedata, GdkEventButton *event,
242 int col, int dx)
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,
262 pagedata, 0,
263 sensitivity_filter,
264 (GdkEvent*)event);
267 static gint
268 cb_treeview_button_press (GtkWidget *treeview,
269 GdkEventButton *event,
270 StfDialogData *pagedata)
272 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
273 int dx, col;
274 stf_preview_find_column (pagedata->fixed.renderdata, (int)event->x, &col, &dx);
275 make_new_column (pagedata, col, dx, FALSE);
276 return TRUE;
279 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
280 int dx, col;
281 stf_preview_find_column (pagedata->fixed.renderdata, (int)event->x, &col, &dx);
282 fixed_context_menu (pagedata, event, col, dx);
283 return TRUE;
286 return FALSE;
290 static gint
291 cb_col_button_press (GtkWidget *button,
292 GdkEventButton *event,
293 gpointer _col)
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;
300 int offset;
301 /* Split column. */
303 /* Correct for indentation of button. */
304 gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (button)),
305 &bca);
306 gtk_widget_get_allocation (button, &ba);
307 offset = bca.x - ba.x;
308 make_new_column (data, col, (int)event->x - offset, FALSE);
310 return TRUE;
313 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
314 GtkAllocation bca, ba;
315 int offset;
317 /* Correct for indentation of button. */
318 gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (button)),
319 &bca);
320 gtk_widget_get_allocation (button, &ba);
321 offset = bca.x - ba.x;
323 fixed_context_menu (data, event, col, (int)event->x - offset);
324 return TRUE;
327 return FALSE;
330 static gint
331 cb_col_key_press (GtkWidget *button,
332 GdkEventKey *event,
333 gpointer _col)
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) {
340 case GDK_KEY_plus:
341 case GDK_KEY_KP_Add:
342 case GDK_KEY_greater:
343 widen_column (data, col, FALSE);
344 return TRUE;
346 case GDK_KEY_minus:
347 case GDK_KEY_KP_Subtract:
348 case GDK_KEY_less:
349 narrow_column (data, col, FALSE);
350 return TRUE;
352 case GDK_KEY_Left:
353 case GDK_KEY_Up:
354 select_column (data, col - 1);
355 return TRUE;
357 case GDK_KEY_Right:
358 case GDK_KEY_Down:
359 select_column (data, col + 1);
360 return TRUE;
362 default:
363 ; /* Nothing. */
367 return FALSE;
371 * fixed_page_update_preview
372 * @pagedata: mother struct
374 * Will simply update the preview
376 * returns : nothing
378 static void
379 fixed_page_update_preview (StfDialogData *pagedata)
381 StfParseOptions_t *parseoptions = pagedata->parseoptions;
382 RenderData_t *renderdata = pagedata->fixed.renderdata;
383 int i;
384 GStringChunk *lines_chunk;
385 GPtrArray *lines;
386 StfTrimType_t trim;
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);
404 GtkWidget *button =
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",
411 NULL);
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 /*************************************************************************************************
425 * SIGNAL HANDLERS
426 *************************************************************************************************/
429 * fixed_page_clear_clicked:
430 * @button: GtkButton
431 * @data: mother struct
433 * Will clear all entries in fixed_collist
435 static void
436 fixed_page_clear_clicked (G_GNUC_UNUSED GtkButton *button,
437 StfDialogData *data)
439 stf_parse_options_fixed_splitpositions_clear (data->parseoptions);
440 fixed_page_update_preview (data);
444 * fixed_page_auto_clicked:
445 * @button: GtkButton
446 * @data: mother struct
448 * Will try to automatically recognize columns in the
449 * text.
451 static void
452 fixed_page_auto_clicked (G_GNUC_UNUSED GtkButton *button,
453 StfDialogData *data)
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
466 void
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);
473 static void
474 queue_redraw (GtkWidget *widget, int x)
476 int hh, xo;
477 GtkAllocation a;
479 if (x < 0)
480 return;
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,
487 x + xo, hh,
488 1, a.height - hh);
492 static gboolean
493 cb_treeview_motion (GtkWidget *widget,
494 GdkEventMotion *event,
495 StfDialogData *pagedata)
497 int x = (int)event->x;
498 int col, dx;
499 RenderData_t *renderdata = pagedata->fixed.renderdata;
500 int old_ruler_x = pagedata->fixed.ruler_x;
501 int colstart, colend, colwidth;
502 gpointer user;
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)) {
509 int ewx;
510 gdk_window_get_position (event->window, &ewx, NULL);
511 x += ewx;
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) {
525 int padx;
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)
537 return FALSE;
539 queue_redraw (widget, old_ruler_x);
540 queue_redraw (widget, pagedata->fixed.ruler_x);
542 return FALSE;
546 static gboolean
547 cb_treeview_draw (GtkWidget *widget,
548 cairo_t *cr,
549 StfDialogData *pagedata)
551 int ruler_x = pagedata->fixed.ruler_x;
552 int height;
553 GtkAllocation a;
554 GdkWindow *bin_window;
555 GdkRGBA ruler_color;
556 GtkStyleContext *context;
558 if (ruler_x < 0)
559 return FALSE;
561 bin_window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
562 if (!gtk_cairo_should_draw_window (cr, bin_window))
563 return FALSE;
565 gtk_widget_get_allocation (widget, &a);
566 height = a.height;
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,
572 &ruler_color);
573 gtk_style_context_restore (context);
575 cairo_save (cr);
576 cairo_rectangle (cr, ruler_x, 0, ruler_x + 1, height);
577 cairo_clip (cr);
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);
581 cairo_stroke (cr);
582 cairo_restore (cr);
584 return FALSE;
587 /*************************************************************************************************
588 * FIXED EXPORTED FUNCTIONS
589 *************************************************************************************************/
592 * stf_dialog_fixed_page_cleanup
593 * @pagedata: mother struct
595 * Will cleanup fixed page run-time data
597 * returns : nothing
599 void
600 stf_dialog_fixed_page_cleanup (StfDialogData *pagedata)
602 stf_preview_free (pagedata->fixed.renderdata);
605 void
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"));
618 /* Set properties */
619 renderdata = pagedata->fixed.renderdata =
620 stf_preview_new (pagedata->fixed.fixed_data_container,
621 NULL);
622 pagedata->fixed.ruler_x = -1;
624 /* Connect signals */
625 g_signal_connect (G_OBJECT (pagedata->fixed.fixed_clear),
626 "clicked",
627 G_CALLBACK (fixed_page_clear_clicked), pagedata);
628 g_signal_connect (G_OBJECT (pagedata->fixed.fixed_auto),
629 "clicked",
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),
639 "draw",
640 G_CALLBACK (cb_treeview_draw), pagedata);