dialog-stf-fixed-page.c 14.1 KB
Newer Older
1
/*
Morten Welinder's avatar
Morten Welinder committed
2
 * dialog-stf-fixed-page.c : Controls the widgets on the fixed page of the dialog (fixed-width page that is)
3
 *
4 5
 * Copyright 2001 Almer S. Tigelaar <almer@gnome.org>
 * Copyright 2003 Morten Welinder <terra@gnome.org>
6
 *
jpekka's avatar
jpekka committed
7 8 9 10 11 12 13 14 15 16 17 18 19
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 21
 */

22
#include <gnumeric-config.h>
23
#include <glib/gi18n.h>
24
#include <gnumeric.h>
25
#include "dialog-stf.h"
26
#include <gui-util.h>
27
#include <gdk/gdkkeysyms.h>
28
#include <gtk/gtkstock.h>
29 30 31 32 33

/*************************************************************************************************
 * MISC UTILITY FUNCTIONS
 *************************************************************************************************/

34 35 36
/**
 * fixed_page_autodiscover:
 * @pagedata: a mother struct
37
 *
38 39 40 41
 * Use the STF's autodiscovery function and put the
 * result in the fixed_collist
 **/
static void
42
fixed_page_autodiscover (StfDialogData *pagedata)
43
{
44
	stf_parse_options_fixed_autodiscover (pagedata->parseoptions,
45
					      pagedata->cur, pagedata->cur_end);
46

47
	if (pagedata->parseoptions->splitpositions->len <= 1) {
48 49 50 51 52
		GtkWidget *dialog = gtk_message_dialog_new (NULL,
			GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_MESSAGE_INFO,
			GTK_BUTTONS_OK,
			_("Autodiscovery did not find any columns in the text. Try manually"));
Jody Goldberg's avatar
Jody Goldberg committed
53
		go_gtk_dialog_run (GTK_DIALOG (dialog), GTK_WINDOW (pagedata->dialog));
54 55 56
	}
}

57
static void fixed_page_update_preview (StfDialogData *pagedata);
58

59 60 61 62 63 64 65 66 67
enum {
	CONTEXT_STF_IMPORT_MERGE_LEFT = 1,
	CONTEXT_STF_IMPORT_MERGE_RIGHT = 2,
	CONTEXT_STF_IMPORT_SPLIT = 3,
	CONTEXT_STF_IMPORT_WIDEN = 4,
	CONTEXT_STF_IMPORT_NARROW = 5
};

static GnumericPopupMenuElement const popup_elements[] = {
68
	{ N_("Merge with column on _left"), GTK_STOCK_REMOVE,
69
	  0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT },
70
	{ N_("Merge with column on _right"), GTK_STOCK_REMOVE,
71 72
	  0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT },
	{ "", NULL, 0, 0, 0 },
73
	{ N_("_Split this column"), NULL,
74 75
	  0, 1 << CONTEXT_STF_IMPORT_SPLIT, CONTEXT_STF_IMPORT_SPLIT },
	{ "", NULL, 0, 0, 0 },
76
	{ N_("_Widen this column"), GTK_STOCK_GO_FORWARD,
77
	  0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN },
78
	{ N_("_Narrow this column"), GTK_STOCK_GO_BACK,
79 80 81 82 83 84 85
	  0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW },
	{ NULL, NULL, 0, 0, 0 },
};


static gboolean
make_new_column (StfDialogData *pagedata, int col, int dx, gboolean test_only)
86 87 88 89
{
	PangoLayout *layout;
	PangoFontDescription *font_desc;
	int charindex, width;
90 91 92 93 94 95 96 97
	RenderData_t *renderdata = pagedata->fixed.renderdata;
	GtkCellRenderer *cell =	stf_preview_get_cell_renderer (renderdata, col);
	int colstart, colend;

	colstart = (col == 0)
		? 0
		: stf_parse_options_fixed_splitpositions_nth (pagedata->parseoptions, col - 1);
	colend = stf_parse_options_fixed_splitpositions_nth (pagedata->parseoptions, col);
98 99

	g_object_get (G_OBJECT (cell), "font_desc", &font_desc, NULL);
100
	layout = gtk_widget_create_pango_layout (GTK_WIDGET (renderdata->tree_view), "x");
101 102 103 104 105 106 107 108
	pango_layout_set_font_description (layout, font_desc);
	pango_layout_get_pixel_size (layout, &width, NULL);
	if (width < 1) width = 1;
	charindex = colstart + (dx + width / 2) / width;
	g_object_unref (layout);
	pango_font_description_free (font_desc);

	if (charindex <= colstart || (colend != -1 && charindex >= colend))
109
		return FALSE;
110

111 112 113
	if (!test_only) {
		stf_parse_options_fixed_splitpositions_add (pagedata->parseoptions, charindex);
		fixed_page_update_preview (pagedata);
114 115
	}

116 117 118 119 120 121 122 123
	return TRUE;
}


static gboolean
widen_column (StfDialogData *pagedata, int col, gboolean test_only)
{
	int colcount = stf_parse_options_fixed_splitpositions_count (pagedata->parseoptions);
Morten Welinder's avatar
Morten Welinder committed
124
	int nextstart, nextnextstart;
125 126 127 128 129 130

	if (col >= colcount - 1)
		return FALSE;

	nextstart = stf_parse_options_fixed_splitpositions_nth (pagedata->parseoptions, col);

Morten Welinder's avatar
Morten Welinder committed
131 132 133
	nextnextstart = (col == colcount - 2)
		? pagedata->longest_line
		: stf_parse_options_fixed_splitpositions_nth (pagedata->parseoptions, col + 1);
134

Morten Welinder's avatar
Morten Welinder committed
135 136
	if (nextstart + 1 >= nextnextstart)
		return FALSE;
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225

	if (!test_only) {
		stf_parse_options_fixed_splitpositions_remove (pagedata->parseoptions, nextstart);
		stf_parse_options_fixed_splitpositions_add (pagedata->parseoptions, nextstart + 1);
		fixed_page_update_preview (pagedata);
	}
	return TRUE;
}

static gboolean
narrow_column (StfDialogData *pagedata, int col, gboolean test_only)
{
	int colcount = stf_parse_options_fixed_splitpositions_count (pagedata->parseoptions);
	int thisstart, nextstart;

	if (col >= colcount - 1)
		return FALSE;

	thisstart = (col == 0)
		? 0
		: stf_parse_options_fixed_splitpositions_nth (pagedata->parseoptions, col - 1);
	nextstart = stf_parse_options_fixed_splitpositions_nth (pagedata->parseoptions, col);

	if (nextstart - 1 <= thisstart)
		return FALSE;

	if (!test_only) {
		stf_parse_options_fixed_splitpositions_remove (pagedata->parseoptions, nextstart);
		stf_parse_options_fixed_splitpositions_add (pagedata->parseoptions, nextstart - 1);
		fixed_page_update_preview (pagedata);
	}
	return TRUE;
}

static gboolean
delete_column (StfDialogData *pagedata, int col, gboolean test_only)
{
	int colcount = stf_parse_options_fixed_splitpositions_count (pagedata->parseoptions);
	if (col < 0 || col >= colcount - 1)
		return FALSE;

	if (!test_only) {
		int nextstart = stf_parse_options_fixed_splitpositions_nth (pagedata->parseoptions, col);
		stf_parse_options_fixed_splitpositions_remove (pagedata->parseoptions, nextstart);
		fixed_page_update_preview (pagedata);
	}
	return TRUE;
}

static void
select_column (StfDialogData *pagedata, int col)
{
	int colcount = stf_parse_options_fixed_splitpositions_count (pagedata->parseoptions);
	GtkTreeViewColumn *column;

	if (col < 0 || col >= colcount)
		return;

	column = stf_preview_get_column (pagedata->fixed.renderdata, col);
	gtk_widget_grab_focus (column->button);
}

static gboolean
fixed_context_menu_handler (GnumericPopupMenuElement const *element,
			    gpointer user_data)
{
	StfDialogData *pagedata = user_data;
	int col = pagedata->fixed.context_col;

	switch (element->index) {
	case CONTEXT_STF_IMPORT_MERGE_LEFT:
		delete_column (pagedata, col - 1, FALSE);
		break;
	case CONTEXT_STF_IMPORT_MERGE_RIGHT:
		delete_column (pagedata, col, FALSE);
		break;
	case CONTEXT_STF_IMPORT_SPLIT:
		make_new_column (pagedata, col, pagedata->fixed.context_dx, FALSE);
		break;
	case CONTEXT_STF_IMPORT_WIDEN:
		widen_column (pagedata, col, FALSE);
		break;
	case CONTEXT_STF_IMPORT_NARROW:
		narrow_column (pagedata, col, FALSE);
		break;
	default:
		; /* Nothing */
	};
	return TRUE;
226 227
}

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
static void
fixed_context_menu (StfDialogData *pagedata, GdkEventButton *event,
		    int col, int dx)
{
	int sensitivity_filter = 0;

	pagedata->fixed.context_col = col;
	pagedata->fixed.context_dx = dx;

	if (!delete_column (pagedata, col - 1, TRUE))
		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT);
	if (!delete_column (pagedata, col, TRUE))
		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_RIGHT);
	if (!make_new_column (pagedata, col, dx, TRUE))
		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_SPLIT);
	if (!widen_column (pagedata, col, TRUE))
		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_WIDEN);
	if (!narrow_column (pagedata, col, TRUE))
		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_NARROW);

	select_column (pagedata, col);
	gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler,
				    pagedata, 0,
				    sensitivity_filter, event);
}

254
static gint
255 256 257
cb_treeview_button_press (GtkWidget *treeview,
			  GdkEventButton *event,
			  StfDialogData *pagedata)
258
{
259
	if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
260
		int dx, col;
261
		stf_preview_find_column (pagedata->fixed.renderdata, (int)event->x, &col, &dx);
262 263
		make_new_column (pagedata, col, dx, FALSE);
		return TRUE;
264
	}
265

266 267
	if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
		int dx, col;
268
		stf_preview_find_column (pagedata->fixed.renderdata, (int)event->x, &col, &dx);
269 270 271 272
		fixed_context_menu (pagedata, event, col, dx);
		return TRUE;
	}

273 274 275 276
	return FALSE;
}


277
static gint
278 279 280
cb_col_button_press (GtkWidget *button,
		     GdkEventButton *event,
		     gpointer   _col)
281 282
{
	int col = GPOINTER_TO_INT (_col);
283 284 285 286 287 288 289 290 291 292 293
	StfDialogData *data = g_object_get_data (G_OBJECT (button), "fixed-data");

	if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
		/* Split column.  */

		/* Correct for indentation of button.  */
		int offset = GTK_BIN (button)->child->allocation.x - button->allocation.x;
		make_new_column (data, col, (int)event->x - offset, FALSE);

		return TRUE;
	}
294

295 296 297 298
	if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
		/* Correct for indentation of button.  */
		int offset = GTK_BIN (button)->child->allocation.x - button->allocation.x;

299
		fixed_context_menu (data, event, col, (int)event->x - offset);
300 301 302 303 304
		return TRUE;
	}

	return FALSE;
}
305

306 307 308 309 310 311 312 313 314 315 316 317 318 319
static gint
cb_col_key_press (GtkWidget *button,
		  GdkEventKey *event,
		  gpointer   _col)
{
	int col = GPOINTER_TO_INT (_col);
	StfDialogData *data = g_object_get_data (G_OBJECT (button), "fixed-data");

	if (event->type == GDK_KEY_PRESS) {
		switch (event->keyval) {
		case GDK_plus:
		case GDK_KP_Add:
		case GDK_greater:
			widen_column (data, col, FALSE);
320 321
			return TRUE;

322 323 324 325 326 327 328 329 330 331
		case GDK_minus:
		case GDK_KP_Subtract:
		case GDK_less:
			narrow_column (data, col, FALSE);
			return TRUE;

		case GDK_Left:
		case GDK_Up:
			select_column (data, col - 1);
			return TRUE;
332

333 334 335
		case GDK_Right:
		case GDK_Down:
			select_column (data, col + 1);
336
			return TRUE;
337 338 339

		default:
			; /*  Nothing.  */
340 341
		}
	}
342

343 344 345
	return FALSE;
}

346 347 348 349 350 351 352 353 354
/**
 * fixed_page_update_preview
 * @pagedata : mother struct
 *
 * Will simply update the preview
 *
 * returns : nothing
 **/
static void
355
fixed_page_update_preview (StfDialogData *pagedata)
356
{
357
	StfParseOptions_t *parseoptions = pagedata->parseoptions;
Morten Welinder's avatar
Morten Welinder committed
358
	RenderData_t *renderdata = pagedata->fixed.renderdata;
359
	int i;
360
	GStringChunk *lines_chunk;
361 362
	GPtrArray *lines;
	StfTrimType_t trim;
363

364 365
	lines_chunk = g_string_chunk_new (100 * 1024);

366
	/* Don't trim on this page.  */
367
	trim = parseoptions->trim_spaces;
368
	stf_parse_options_set_trim_spaces (parseoptions, TRIM_TYPE_NEVER);
369
	lines = stf_parse_general (parseoptions, lines_chunk,
370 371
				   pagedata->cur, pagedata->cur_end,
				   LINE_DISPLAY_LIMIT);
372 373
	stf_parse_options_set_trim_spaces (parseoptions, trim);

374
	stf_preview_set_lines (renderdata, lines_chunk, lines);
375

376 377 378 379 380
	for (i = 0; i < renderdata->colcount; i++) {
		GtkTreeViewColumn *column =
			stf_preview_get_column (renderdata, i);
		GtkCellRenderer *cell =
			stf_preview_get_cell_renderer (renderdata, i);
381

382 383
		gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);

384 385 386 387 388 389
		g_object_set (G_OBJECT (cell),
			      "family", "monospace",
			      NULL);

		g_object_set_data (G_OBJECT (column->button), "fixed-data", pagedata);
		g_object_set (G_OBJECT (column), "clickable", TRUE, NULL);
390 391 392 393 394
		g_signal_connect (column->button, "button_press_event",
				  G_CALLBACK (cb_col_button_press),
				  GINT_TO_POINTER (i));
		g_signal_connect (column->button, "key_press_event",
				  G_CALLBACK (cb_col_key_press),
395 396
				  GINT_TO_POINTER (i));
	}
397 398 399 400 401 402
}

/*************************************************************************************************
 * SIGNAL HANDLERS
 *************************************************************************************************/

403 404 405 406
/**
 * fixed_page_clear_clicked:
 * @button: GtkButton
 * @data: mother struct
407
 *
408 409 410
 * Will clear all entries in fixed_collist
 **/
static void
Morten Welinder's avatar
Morten Welinder committed
411
fixed_page_clear_clicked (G_GNUC_UNUSED GtkButton *button,
412
			  StfDialogData *data)
413
{
414
	stf_parse_options_fixed_splitpositions_clear (data->parseoptions);
415 416 417 418 419 420 421
	fixed_page_update_preview (data);
}

/**
 * fixed_page_auto_clicked:
 * @button: GtkButton
 * @data: mother struct
422
 *
423 424 425 426
 * Will try to automatically recognize columns in the
 * text.
 **/
static void
Morten Welinder's avatar
Morten Welinder committed
427
fixed_page_auto_clicked (G_GNUC_UNUSED GtkButton *button,
428
			 StfDialogData *data)
429 430 431 432 433 434
{
	fixed_page_autodiscover (data);

	fixed_page_update_preview (data);
}

435
/**
436
 * stf_dialog_fixed_page_prepare
437 438 439 440 441 442
 * @data : mother struct
 *
 * Will prepare the fixed page
 *
 * returns : nothing
 **/
443 444
void
stf_dialog_fixed_page_prepare (StfDialogData *pagedata)
445
{
446
	stf_parse_options_set_trim_spaces (pagedata->parseoptions, TRIM_TYPE_NEVER);
447 448 449
	fixed_page_update_preview (pagedata);
}

450 451 452 453
/*************************************************************************************************
 * FIXED EXPORTED FUNCTIONS
 *************************************************************************************************/

454
/**
455
 * stf_dialog_fixed_page_cleanup
456 457 458 459 460 461 462
 * @pagedata : mother struct
 *
 * Will cleanup fixed page run-time data
 *
 * returns : nothing
 **/
void
463
stf_dialog_fixed_page_cleanup (StfDialogData *pagedata)
464
{
Morten Welinder's avatar
Morten Welinder committed
465
	stf_preview_free (pagedata->fixed.renderdata);
466 467 468
}

void
469
stf_dialog_fixed_page_init (GladeXML *gui, StfDialogData *pagedata)
470 471 472 473
{

	g_return_if_fail (gui != NULL);
	g_return_if_fail (pagedata != NULL);
474

475
        /* Create/get object and fill information struct */
476 477 478
	pagedata->fixed.fixed_clear   = GTK_BUTTON      (glade_xml_get_widget (gui, "fixed_clear"));
	pagedata->fixed.fixed_auto    = GTK_BUTTON      (glade_xml_get_widget (gui, "fixed_auto"));
	pagedata->fixed.fixed_data_container =          (glade_xml_get_widget (gui, "fixed_data_container"));
479 480

	/* Set properties */
Morten Welinder's avatar
Morten Welinder committed
481
	pagedata->fixed.renderdata    =
482 483
		stf_preview_new (pagedata->fixed.fixed_data_container,
				 NULL);
484

485
	/* Connect signals */
486
	g_signal_connect (G_OBJECT (pagedata->fixed.fixed_clear),
487 488
		"clicked",
		G_CALLBACK (fixed_page_clear_clicked), pagedata);
489
	g_signal_connect (G_OBJECT (pagedata->fixed.fixed_auto),
490 491
		"clicked",
		G_CALLBACK (fixed_page_auto_clicked), pagedata);
492
	g_signal_connect (G_OBJECT (pagedata->fixed.renderdata->tree_view),
493 494
		"button_press_event",
		 G_CALLBACK (cb_treeview_button_press), pagedata);
495
}